/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Order.c
 *
 * Functions for setting the orders of a droid or group of droids
 *
 */
#include <string.h>

#include "lib/framework/frame.h"
#include "lib/framework/input.h"
#include "lib/framework/math-help.h"

#include "objects.h"
#include "order.h"
#include "action.h"
#include "map.h"
#include "formationdef.h"
#include "formation.h"
#include "geometry.h"
#include "projectile.h"
#include "effects.h"	// for waypoint display
#include "lib/gamelib/gtime.h"
#include "intorder.h"
#include "orderdef.h"
#include "transporter.h"
#include "group.h"
#include "cmddroid.h"
#include "lib/script/script.h"
#include "scripttabs.h"
#include "scriptcb.h"

#include "multiplay.h"  //ajl

#include "mission.h"
#include "hci.h"
#include "visibility.h"
#include "display.h"
#include "ai.h"
#include "warcam.h"
#include "lib/sound/audio_id.h"
#include "lib/sound/audio.h"
#include "fpath.h"
#include "display3d.h"
#include "combat.h"

// how long to run for
#define RUN_TIME		8000
#define RUN_BURN_TIME	10000

// how far to move away from something that is being defended
#define DEFEND_MAXDIST		(TILE_UNITS * 3) //5)
#define DEFEND_BASEDIST		(TILE_UNITS * 3)
#define DEFEND_CMD_MAXDIST		(TILE_UNITS * 8)
#define DEFEND_CMD_BASEDIST		(TILE_UNITS * 5)

// how big an area for a repair droid to cover
#define REPAIR_MAXDIST		(TILE_UNITS * 5)
// how big an area for a constructor droid to cover
#define CONSTRUCT_MAXDIST		(TILE_UNITS * 8)

// how close to the target a droid has to be to be for DORDER_SCOUT to end
#define SCOUT_DIST			(TILE_UNITS * 8)

// how far a droid can wander when attacking during a DORDER_SCOUT
#define SCOUT_ATTACK_DIST	(TILE_UNITS * 5)

// retreat positions for the players
RUN_DATA	asRunData[MAX_PLAYERS];

// deal with a droid receiving a primary order
BOOL secondaryGotPrimaryOrder(DROID *psDroid, DROID_ORDER order);

// check all the orders in the list for died objects
void orderCheckList(DROID *psDroid);

// clear all the orders from the list
void orderClearDroidList(DROID *psDroid);

void orderDroidStatsTwoLocAdd(DROID *psDroid, DROID_ORDER order,
						BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2);

//Watermelon:add a timestamp to order circle
static UDWORD orderStarted;

// whether an order effect has been displayed
static BOOL bOrderEffectDisplayed = false;

//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////

//call this *AFTER* every mission so it gets reset
void initRunData(void)
{
    UBYTE   i;

    for (i = 0; i < MAX_PLAYERS; i++)
    {
        memset(&asRunData[i], 0, sizeof(RUN_DATA));
    }
}

//FIXME: unit doesn't shoot while returning to the guard position
// check whether a droid has to move back to the thing it is guarding
static void orderCheckGuardPosition(DROID *psDroid, SDWORD range)
{
	SDWORD		xdiff, ydiff;
	UDWORD		x,y;

	if (psDroid->psTarget != NULL)
	{
		//if ((psDroid->droidType != DROID_REPAIR) && // repair droids always follow behind - don't want them jumping into the line of fire
        // repair droids always follow behind - don't want them jumping into the line of fire
        if ((!(psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
            DROID_CYBORG_REPAIR)) && psDroid->psTarget->type == OBJ_DROID &&
			orderStateLoc((DROID *)psDroid->psTarget, DORDER_MOVE, &x,&y))
		{
			// got a moving droid - check against where the unit is going
			psDroid->orderX = (UWORD)x;
			psDroid->orderY = (UWORD)y;
		}
		else
		{
			psDroid->orderX = psDroid->psTarget->pos.x;
			psDroid->orderY = psDroid->psTarget->pos.y;
		}
	}

	xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
	ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
	if (xdiff*xdiff + ydiff*ydiff > range*range)
	{
		if ((psDroid->sMove.Status != MOVEINACTIVE) &&
			((psDroid->action == DACTION_MOVE) ||
			 (psDroid->action == DACTION_MOVEFIRE)))
		{
			xdiff = (SDWORD)psDroid->sMove.DestinationX - (SDWORD)psDroid->orderX;
			ydiff = (SDWORD)psDroid->sMove.DestinationY - (SDWORD)psDroid->orderY;
			if (xdiff*xdiff + ydiff*ydiff > range*range)
			{
 				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX, psDroid->orderY);
			}
		}
		else
		{
			actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX, psDroid->orderY);
		}
	}
}

/*For a given repair droid, check if there are any damaged droids within
a defined range*/
BASE_OBJECT * checkForRepairRange(DROID *psDroid,DROID *psTarget)
{
	DROID		*psCurr;
	SDWORD		xdiff, ydiff;

	ASSERT( psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
        DROID_CYBORG_REPAIR, "checkForRepairRange:Invalid droid type" );

	if(psTarget != NULL
		&& psTarget->died)
	{
		psTarget = NULL;
	}

	// if guarding a unit - always check that first
	psCurr = (DROID*)orderStateObj(psDroid, DORDER_GUARD);
	if (psCurr != NULL
	 && psCurr->type == OBJ_DROID
	 && droidIsDamaged(psCurr))
	{
		return (BASE_OBJECT *)psCurr;
	}

	if ((psTarget != NULL) &&
		(psTarget->type == OBJ_DROID) &&
		(psTarget->player == psDroid->player))
	{
		psCurr = psTarget->psNext;
	}
	else
	{
		psCurr = apsDroidLists[psDroid->player];
	}
	for (; psCurr != NULL; psCurr = psCurr->psNext)
	{
		//check for damage
		if (droidIsDamaged(psCurr) &&
			visibleObject((BASE_OBJECT *)psDroid, (BASE_OBJECT *)psCurr))
		{
			//check for within range
			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psCurr->pos.x;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psCurr->pos.y;
			if ( (xdiff*xdiff) + (ydiff*ydiff) < REPAIR_MAXDIST*REPAIR_MAXDIST)
			{
				return (BASE_OBJECT *)psCurr;
			}
		}
	}
	return NULL;
}

/*For a given constructor droid, check if there are any damaged buildings within
a defined range*/
BASE_OBJECT * checkForDamagedStruct(DROID *psDroid, STRUCTURE *psTarget)
{
	STRUCTURE		*psCurr;
	SDWORD			xdiff, ydiff;

	ASSERT(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT, "Invalid unit type");

	if(psTarget != NULL
		&& psTarget->died)
	{
		psTarget = NULL;
	}

	if ((psTarget != NULL) &&
		(psTarget->type == OBJ_STRUCTURE) &&
		(psTarget->player == psDroid->player))
	{
		psCurr = psTarget->psNext;
	}
	else
	{
		psCurr = apsStructLists[psDroid->player];
	}
	for (; psCurr != NULL; psCurr = psCurr->psNext)
	{
		//check for damage
		if (structIsDamaged(psCurr) &&
			visibleObject((BASE_OBJECT *)psDroid, (BASE_OBJECT *)psCurr))
		{
			//check for within range
			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psCurr->pos.x;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psCurr->pos.y;
			//check for repair distance and not construct_dist - this allows for structures being up to 3 tiles across
			if ((xdiff*xdiff) + (ydiff*ydiff) < REPAIR_MAXDIST*REPAIR_MAXDIST)
			{
				return (BASE_OBJECT *)psCurr;
			}
		}
	}
	return NULL;
}

/* Update a droids order state */
void orderUpdateDroid(DROID *psDroid)
{
	BASE_OBJECT		*psObj;
	STRUCTURE		*psStruct, *psWall;
	REPAIR_FACILITY	*psRepairFac;
	SDWORD			xdiff,ydiff, temp;
	SECONDARY_STATE state;
	BOOL			bAttack;
	//Watermelon:int i
	UBYTE i;
	float			radToAction;
	SDWORD			xoffset,yoffset;

	// clear the target if it has died
	if (psDroid->psTarget && psDroid->psTarget->died)
	{
		setDroidTarget(psDroid, NULL);
	}

	//clear its base struct if its died
	if (psDroid->psBaseStruct && psDroid->psBaseStruct->died)
	{
		setDroidBase(psDroid, NULL);
	}

	// check for died objects in the list
	orderCheckList(psDroid);

	switch (psDroid->order)
	{
	case DORDER_NONE:
		psObj = NULL;
		// see if there are any orders queued up
		if (orderDroidList(psDroid))
		{
			// started a new order, quit
			break;
		}
		// HACK: we always want to update orders when NOT running a MP game,
		// and we don't want to update when the droid belongs to another human player
		else if (!myResponsibility(psDroid->player) && bMultiPlayer
					&& isHumanPlayer(psDroid->player))
		{
			return;
		}
		// if you are in a command group, default to guarding the commander
		else if (hasCommander(psDroid) &&
				 (psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat()))  // stop the constructor auto repairing when it is about to demolish
		{
			orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psDroid->psGroup->psCommander);
		}
		else if (psDroid->droidType == DROID_TRANSPORTER)
		{
			//check transporter isn't sitting there waiting to be filled when nothing exists!
			if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag()
			    && !missionDroidsRemaining(selectedPlayer))
			{
				// check that nothing is on the transporter (transporter counts as first in group)
				if (psDroid->psGroup && psDroid->psGroup->refCount < 2)
				{
					// the script can call startMission for this callback for offworld missions
					eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
				}
			}
		}

		// default to guarding if the correct secondary order is set
		else if ((psDroid->player == selectedPlayer) &&
			(psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat()) && // stop the constructor auto repairing when it is about to demolish
			secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
			!isVtolDroid(psDroid) &&
			state == DSS_HALT_GUARD)
		{
			UDWORD actionX = psDroid->pos.x;
			UDWORD actionY = psDroid->pos.y;

			orderDroidLoc(psDroid, DORDER_GUARD, actionX,actionY);
		}

		//repair droids default to repairing droids within a given range
		else if ((psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
            DROID_CYBORG_REPAIR) &&	!orderState(psDroid, DORDER_GUARD))
		{
			psObj = checkForRepairRange(psDroid,NULL);
			if (psObj && (!bMultiPlayer || myResponsibility(psDroid->player)))
			{
				orderDroidObj(psDroid, DORDER_DROIDREPAIR, psObj);
			}
		}

		//constructor droids default to repairing structures within a given range
		//else if ((psDroid->droidType == DROID_CONSTRUCT) &&
        else if ((psDroid->droidType == DROID_CONSTRUCT ||
            psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
				 !orderState(psDroid, DORDER_GUARD) &&
				 (psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat()))
		{
			psObj = checkForDamagedStruct(psDroid,NULL);
			if (psObj && (!bMultiPlayer || myResponsibility(psDroid->player)))
			{
				orderDroidObj(psDroid, DORDER_REPAIR, psObj);
			}
		}

		break;
	case DORDER_TRANSPORTRETURN:
		if (psDroid->action == DACTION_NONE)
		{
			missionMoveTransporterOffWorld( psDroid );

			/* clear order */
			psDroid->order = DORDER_NONE;
			setDroidTarget(psDroid, NULL);
			psDroid->psTarStats = NULL;
		}
		break;
	case DORDER_TRANSPORTOUT:
		if (psDroid->action == DACTION_NONE)
		{
			//if moving droids to safety and still got some droids left don't do callback
			if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
				missionDroidsRemaining(selectedPlayer))
			{
				//move droids in Transporter into holding list
				moveDroidsToSafety(psDroid);
				//we need the transporter to just sit off world for a while...
				orderDroid( psDroid, DORDER_TRANSPORTIN );
				/* set action transporter waits for timer */
				actionDroid( psDroid, DACTION_TRANSPORTWAITTOFLYIN );

				missionSetReinforcementTime( gameTime );

				//don't do this until waited for the required time
				//fly Transporter back to get some more droids
				//orderDroidLoc( psDroid, DORDER_TRANSPORTIN,
				//    getLandingX(selectedPlayer), getLandingY(selectedPlayer));
			}
			else
			{
			    //the script can call startMission for this callback for offworld missions
			    eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);

			    /* clear order */
			    psDroid->order = DORDER_NONE;
				setDroidTarget(psDroid, NULL);
			    psDroid->psTarStats = NULL;
            }
		}
		break;
	case DORDER_TRANSPORTIN:
		if ( (psDroid->action == DACTION_NONE) &&
			 (psDroid->sMove.Status == MOVEINACTIVE) )
		{
			/* clear order */
			psDroid->order = DORDER_NONE;
			setDroidTarget(psDroid, NULL);
			psDroid->psTarStats = NULL;

//FFS! You only wan't to do this if the droid being tracked IS the transporter! Not all the time!
// What about if your happily playing the game and tracking a droid, and re-enforcements come in!
// And suddenly BLAM!!!! It drops you out of camera mode for no apparent reason! TOTALY CONFUSING
// THE PLAYER!
//
// Just had to get that off my chest....end of rant.....
//
			if( psDroid == getTrackingDroid() ) { // Thats better...
				/* deselect transporter if have been tracking */
	    		if ( getWarCamStatus() )
		    	{
			    	camToggleStatus();
			    }
			}

			DeSelectDroid(psDroid);

            /*don't try the unload if moving droids to safety and still got some
            droids left  - wait until full and then launch again*/
            if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
                missionDroidsRemaining(selectedPlayer))
            {
                resetTransporter(psDroid);
            }
            else
            {
			    unloadTransporter( psDroid, psDroid->pos.x, psDroid->pos.y, false );
            }
		}
		break;
	case DORDER_MOVE:
	case DORDER_RETREAT:
	case DORDER_DESTRUCT:
		// Just wait for the action to finish then clear the order
		if (psDroid->action == DACTION_NONE)
		{
			psDroid->order = DORDER_NONE;
			setDroidTarget(psDroid, NULL);
			psDroid->psTarStats = NULL;
		}
		break;
	case DORDER_RECOVER:
		if (psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
		}
		else if (psDroid->action == DACTION_NONE)
		{
			// stoped moving, but still havn't got the artifact
			actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
		}
		break;
	case DORDER_MOVE_ATTACKWALL:
	case DORDER_SCOUT_ATTACKWALL:
		//Watermelon:check against all weapons now
		for(i = 0;i <psDroid->numWeaps;i++)
		{
			if (psDroid->psTarget == NULL)
			{
				if (psDroid->order == DORDER_MOVE_ATTACKWALL)
				{
					psDroid->order = DORDER_MOVE;
				}
				else
				{
					psDroid->order = DORDER_SCOUT;
				}
				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
			}
			else if ((((psDroid->action != DACTION_ATTACK) &&
					   (psDroid->action != DACTION_MOVETOATTACK) &&
					   (psDroid->action != DACTION_ROTATETOATTACK)) ||
					  (psDroid->psActionTarget[0] != psDroid->psTarget)) &&
					 actionInRange(psDroid, psDroid->psTarget, 0) )
			{
				actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
			}
			else if (psDroid->action == DACTION_NONE)
			{
				if (psDroid->order == DORDER_SCOUT_ATTACKWALL)
				{
					psDroid->order = DORDER_SCOUT;
				}
				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
			}
		}
		break;
	case DORDER_SCOUT:
	case DORDER_PATROL:
		// if there is an enemy around, attack it
		if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) &&
			(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
			 (aiBestNearestTarget(psDroid, &psObj, 0) >= 0) )
		{
			switch (psDroid->droidType)
			{
			case DROID_WEAPON:
			case DROID_CYBORG:
			case DROID_CYBORG_SUPER:
			case DROID_PERSON:
			case DROID_COMMAND:
				actionDroidObj(psDroid, DACTION_ATTACK, psObj);
				break;
			case DROID_SENSOR:
				actionDroidObj(psDroid, DACTION_OBSERVE, psObj);
				break;
			default:
				actionDroid(psDroid, DACTION_NONE);
				break;
			}
		}
		else if (psDroid->action == DACTION_NONE)
		{
			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
			if (xdiff*xdiff + ydiff*ydiff < SCOUT_DIST*SCOUT_DIST)
			{
				if (psDroid->order == DORDER_PATROL)
				{
					// head back to the other point
					temp = psDroid->orderX;
					psDroid->orderX = psDroid->orderX2;
					psDroid->orderX2 = (UWORD)temp;
					temp = psDroid->orderY;
					psDroid->orderY = psDroid->orderY2;
					psDroid->orderY2 = (UWORD)temp;
					actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
				}
				else
				{
					psDroid->order = DORDER_NONE;
				}
			}
			else
			{
				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
			}
		}
		else if ((psDroid->action == DACTION_ATTACK) ||
				 (psDroid->action == DACTION_MOVETOATTACK) ||
				 (psDroid->action == DACTION_ROTATETOATTACK) ||
				 (psDroid->action == DACTION_OBSERVE) ||
				 (psDroid->action == DACTION_MOVETOOBSERVE))
		{
			// attacking something - see if the droid has gone too far
			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->actionX;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->actionY;
			if (xdiff*xdiff + ydiff*ydiff > SCOUT_ATTACK_DIST*SCOUT_ATTACK_DIST)
			{
				actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->actionX,psDroid->actionY);
			}
		}
		break;
	case DORDER_CIRCLE:
		// if there is an enemy around, attack it
		if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) &&
			(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
			 (aiBestNearestTarget(psDroid, &psObj, 0) >= 0) )
		{
			switch (psDroid->droidType)
			{
			case DROID_WEAPON:
			case DROID_CYBORG:
			case DROID_CYBORG_SUPER:
			case DROID_PERSON:
			case DROID_COMMAND:
				actionDroidObj(psDroid, DACTION_ATTACK, psObj);
				break;
			case DROID_SENSOR:
				actionDroidObj(psDroid, DACTION_OBSERVE, psObj);
				break;
			default:
				actionDroid(psDroid, DACTION_NONE);
				break;
			}
		}
		else if (psDroid->action == DACTION_NONE || psDroid->action == DACTION_MOVE)
		{
			if (psDroid->action == DACTION_MOVE)
			{
				if ( orderStarted && ((orderStarted + 500) > gameTime) )
				{
					break;
				}
				orderStarted = gameTime;
			}
			psDroid->action = DACTION_NONE;

			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
			if (xdiff*xdiff + ydiff*ydiff <= 2000 * 2000)
			{
				if (psDroid->order == DORDER_CIRCLE)
				{
					//Watermelon:use orderX,orderY as local space origin and calculate droid direction in local space
					radToAction = atan2f((float)xdiff, (float)ydiff);
					xoffset = sinf(radToAction) * 1500;
					yoffset = cosf(radToAction) * 1500;
					xdiff = (SDWORD)psDroid->pos.x - (SDWORD)(psDroid->orderX + xoffset);
					ydiff = (SDWORD)psDroid->pos.y - (SDWORD)(psDroid->orderY + yoffset);
					if (xdiff*xdiff + ydiff*ydiff < TILE_UNITS * TILE_UNITS)
					{
						//Watermelon:conter-clockwise 30 degree's per action
						radToAction -= M_PI * 30 / 180;
						xoffset = sinf(radToAction) * 1500;
						yoffset = cosf(radToAction) * 1500;
						actionDroidLoc(psDroid, DACTION_MOVE, (psDroid->orderX + xoffset),(psDroid->orderY + yoffset));
					}
					else
					{
						actionDroidLoc(psDroid, DACTION_MOVE, (psDroid->orderX + xoffset),(psDroid->orderY + yoffset));
					}
				}
				else
				{
					psDroid->order = DORDER_NONE;
				}
			}
		}
		else if ((psDroid->action == DACTION_ATTACK) ||
				 (psDroid->action == DACTION_MOVETOATTACK) ||
				 (psDroid->action == DACTION_ROTATETOATTACK) ||
				 (psDroid->action == DACTION_OBSERVE) ||
				 (psDroid->action == DACTION_MOVETOOBSERVE))
		{
			// attacking something - see if the droid has gone too far
			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->actionX;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->actionY;
			//if (xdiff*xdiff + ydiff*ydiff > psDroid->sMove.iGuardRadius * psDroid->sMove.iGuardRadius)
			if (xdiff*xdiff + ydiff*ydiff > 2000 * 2000)
			{
				// head back to the target location
				actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->orderX,psDroid->orderY);
			}
		}
		break;
	case DORDER_HELPBUILD:
	case DORDER_DEMOLISH:
	case DORDER_OBSERVE:
	case DORDER_REPAIR:
	case DORDER_DROIDREPAIR:
	case DORDER_RESTORE:
	case DORDER_CLEARWRECK:
		if (psDroid->action == DACTION_NONE ||
			psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
			actionDroid(psDroid, DACTION_NONE);
			if (psDroid->player == selectedPlayer)
			{
				intRefreshScreen();
			}
		}
/*		if(psDroid->droidType == DROID_REPAIR
			&& psDroid->action == DACTION_SULK)
		{
			psObj = checkForRepairRange(psDroid,(DROID *)psDroid->psTarget);
			if (psObj)
			{
				orderDroidObj(psDroid, DORDER_DROIDREPAIR, psObj);
				break;
			}
		}*/
		break;
	case DORDER_REARM:
		if ((psDroid->psTarget == NULL) ||
			(psDroid->psActionTarget[0] == NULL) )
		{
			// arm pad destroyed find another
			psDroid->order = DORDER_NONE;
			moveToRearm(psDroid);
		}
		else if (psDroid->action == DACTION_NONE)
		{
			psDroid->order = DORDER_NONE;
		}
		break;
	case DORDER_ATTACK:
	case DORDER_ATTACKTARGET:
		if (psDroid->psTarget == NULL)
		{
			// if vtol then return to rearm pad as long as there are no other
			// orders queued up
			if (isVtolDroid(psDroid))
			{
				if (!orderDroidList(psDroid))
				{
	       			psDroid->order = DORDER_NONE;
					moveToRearm(psDroid);
				}
			}
			else
			{
       			psDroid->order = DORDER_NONE;
        		actionDroid(psDroid, DACTION_NONE);
			}
		}
		else if ( ((psDroid->action == DACTION_MOVE) ||
				   (psDroid->action == DACTION_MOVEFIRE)) &&
				   actionVisibleTarget(psDroid, psDroid->psTarget, 0) && !isVtolDroid(psDroid))
		{
			// moved near enough to attack change to attack action
			actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
		}
		else if ( (psDroid->action == DACTION_MOVETOATTACK) &&
				  !isVtolDroid(psDroid) &&
				  !actionVisibleTarget(psDroid, psDroid->psTarget, 0) )
		{
			// lost sight of the target while chasing it - change to a move action so
			// that the unit will fire on other things while moving
			actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
		}
		else if (!isVtolDroid(psDroid)
		      && psDroid->psTarget == psDroid->psActionTarget[0]
		      && actionInRange(psDroid, psDroid->psTarget, 0)
		      && (psWall = visGetBlockingWall((BASE_OBJECT *)psDroid, psDroid->psTarget))
		      && psWall->player != psDroid->player)
		{
			// there is a wall in the way - attack that
			actionDroidObj(psDroid, DACTION_ATTACK, (BASE_OBJECT *)psWall);
		}
		else if ((psDroid->action == DACTION_NONE) ||
				 (psDroid->action == DACTION_CLEARREARMPAD))
		{
			if ((psDroid->order == DORDER_ATTACKTARGET) &&
				secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD) &&
				!actionInRange(psDroid, psDroid->psTarget, 0) )
			{
				// on hold orders give up
				psDroid->order = DORDER_NONE;
				setDroidTarget(psDroid, NULL);
			}
			else if (!isVtolDroid(psDroid) ||
				allVtolsRearmed(psDroid))
			{
				actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
			}
		}
		break;
	case DORDER_BUILD:
		if (psDroid->action == DACTION_BUILD &&
			psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
			actionDroid(psDroid, DACTION_NONE);
		}
		else if (psDroid->action == DACTION_NONE)
		{
			psDroid->order = DORDER_NONE;
		}
		break;
	case DORDER_EMBARK:
		// only place it can be trapped - in multiPlayer can only put cyborgs onto a Transporter
		if (bMultiPlayer && !cyborgDroid(psDroid))
		{
			psDroid->order = DORDER_NONE;
			actionDroid(psDroid, DACTION_NONE);
		}
		else
		{
			// don't want the droids to go into a formation for this order
			if (psDroid->sMove.psFormation != NULL)
			{
				formationLeave(psDroid->sMove.psFormation, psDroid);
				psDroid->sMove.psFormation = NULL;
			}

			// Wait for the action to finish then assign to Transporter (if not already flying)
			if (psDroid->psTarget == NULL || transporterFlying((DROID *)psDroid->psTarget))
			{
				psDroid->order = DORDER_NONE;
				actionDroid(psDroid, DACTION_NONE);
			}
			else if (abs((SDWORD)psDroid->pos.x - (SDWORD)psDroid->psTarget->pos.x) < TILE_UNITS
			         && abs((SDWORD)psDroid->pos.y - (SDWORD)psDroid->psTarget->pos.y) < TILE_UNITS)
			{
				// if in multiPlayer, only want to process if this player's droid
				if (!bMultiPlayer || psDroid->player == selectedPlayer)
				{
					// save the target of current droid (the transporter)
					DROID * transporter = (DROID *)psDroid->psTarget;

					// order the droid to stop so moveUpdateDroid does not process this unit
					orderDroid(psDroid, DORDER_STOP);
					setDroidTarget(psDroid, NULL);
					psDroid->psTarStats = NULL;
					secondarySetState(psDroid, DSO_RETURN_TO_LOC, DSS_NONE);
					// process orders *before* adding unit to transporter
					// since we remove it from the list!
					transporterAddDroid(transporter, psDroid);
				}
			}
			else if (psDroid->action == DACTION_NONE)
			{
				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
			}
		}

		// Do we need to clear the secondary order "DSO_EMBARK" here? (PD)
		break;
    case DORDER_DISEMBARK:
        //only valid in multiPlayer mode
        if (bMultiPlayer)
        {
            //this order can only be given to Transporter droids
            if (psDroid->droidType == DROID_TRANSPORTER)
            {
                /*once the Transporter has reached its destination (and landed),
                get all the units to disembark*/
                if (psDroid->action == DACTION_NONE && psDroid->sMove.Status ==
                    MOVEINACTIVE && psDroid->sMove.iVertSpeed == 0)
                {
                    //only need to unload if this player's droid
                    if (psDroid->player == selectedPlayer)
                    {
                        unloadTransporter(psDroid, psDroid->pos.x, psDroid->pos.y, false);
                    }
                    //reset the transporter's order
                    psDroid->order = DORDER_NONE;
                }
            }
        }
        break;
	case DORDER_RTB:
		// Just wait for the action to finish then clear the order
		if (psDroid->action == DACTION_NONE)
		{
			psDroid->order = DORDER_NONE;
			secondarySetState(psDroid, DSO_RETURN_TO_LOC, DSS_NONE);
		}
		break;
	case DORDER_LEAVEMAP:
		if ((psDroid->pos.x < TILE_UNITS*2) ||
			(psDroid->pos.x > (mapWidth-2)*TILE_UNITS) ||
			(psDroid->pos.y < TILE_UNITS*2) ||
			(psDroid->pos.y > (mapHeight-2)*TILE_UNITS) ||
			(psDroid->action == DACTION_NONE))
		{
			psDroid->order = DORDER_NONE;
			psScrCBVtolOffMap = psDroid;
			eventFireCallbackTrigger((TRIGGER_TYPE)CALL_VTOL_OFF_MAP);
		}
		break;
	case DORDER_RTR:
	case DORDER_RTR_SPECIFIED:
		if (psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
			actionDroid(psDroid, DACTION_NONE);
		}
		else if (psDroid->action == DACTION_NONE)
		{
			/* get repair facility pointer */
			psStruct = (STRUCTURE *) psDroid->psTarget;
			ASSERT( psStruct != NULL,
				"orderUpdateUnit: invalid structure pointer" );
			psRepairFac = (REPAIR_FACILITY *) psStruct->pFunctionality;
			ASSERT( psRepairFac != NULL,
				"orderUpdateUnit: invalid repair facility pointer" );

			xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psTarget->pos.x;
			ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psTarget->pos.y;
			if (xdiff*xdiff + ydiff*ydiff < (TILE_UNITS*8)*(TILE_UNITS*8))
			{
				/* action droid to wait */
				actionDroid(psDroid, DACTION_WAITFORREPAIR);
			}
			else
			{
				// move the droid closer to the repair facility
				actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
			}
		}
		else if (psDroid->order == DORDER_RTR &&
				 (psDroid->action == DACTION_MOVE ||
				  psDroid->action == DACTION_MOVEFIRE) &&
				  ((psDroid->id % 50) == (frameGetFrameNumber() % 50)))
		{
			// see if there is a repair facility nearer than the one currently selected
			orderDroid(psDroid, DORDER_RTR);
		}
		break;
	case DORDER_RUNBURN:
		if (psDroid->actionStarted + RUN_BURN_TIME < gameTime)
		{
			debug(LOG_DEATH, "orderUpdateDroid: Droid %d burned to death", psDroid->id); // why is this an order?
			destroyDroid( psDroid );
		}
		break;
	case DORDER_RUN:
		if (psDroid->action == DACTION_NONE)
		{
			// got there so stop running
			psDroid->order = DORDER_NONE;
		}
		if (psDroid->actionStarted + RUN_TIME < gameTime)
		{
			// been running long enough
			actionDroid(psDroid, DACTION_NONE);
			psDroid->order = DORDER_NONE;
		}
		break;
	case DORDER_LINEBUILD:
		if (psDroid->action == DACTION_NONE ||
			(psDroid->action == DACTION_BUILD && psDroid->psTarget == NULL))
		{
			// finished building the current structure
			if (map_coord(psDroid->orderX) == map_coord(psDroid->orderX2)
			 && map_coord(psDroid->orderY) == map_coord(psDroid->orderY2))
			{
				// finished all the structures - done
				psDroid->order = DORDER_NONE;
				setDroidTarget(psDroid, NULL);
				psDroid->psTarStats = NULL;
				break;
			}

			// update the position for another structure
			if (map_coord(psDroid->orderX) == map_coord(psDroid->orderX2))
			{
				// still got building to do - working vertically
				if (psDroid->orderY < psDroid->orderY2)
				{
					psDroid->orderY += TILE_UNITS;
				}
				else
				{
					psDroid->orderY -= TILE_UNITS;
				}
			}
			else if (map_coord(psDroid->orderY) == map_coord(psDroid->orderY2))
			{
				// still got building to do - working horizontally
				if (psDroid->orderX < psDroid->orderX2)
				{
					psDroid->orderX += TILE_UNITS;
				}
				else
				{
					psDroid->orderX -= TILE_UNITS;
				}
			}
			else
			{
				ASSERT( false, "orderUpdateUnit: LINEBUILD order on diagonal line" );
				break;
			}

			// build another structure
			setDroidTarget(psDroid, NULL);
			actionDroidLoc(psDroid, DACTION_BUILD, psDroid->orderX,psDroid->orderY);
			//intRefreshScreen();
		}
		break;
	case DORDER_FIRESUPPORT:
		if (psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
			if (isVtolDroid(psDroid))
			{
				moveToRearm(psDroid);
			}
			else
			{
				actionDroid(psDroid, DACTION_NONE);
			}
		}
		//before targetting - check VTOL's are fully armed
		else if (vtolEmpty(psDroid))
		{
			moveToRearm(psDroid);
		}
		//indirect weapon droid attached to (standard)sensor droid
		else
		{
			BASE_OBJECT	*psFireTarget = NULL;

			if (psDroid->psTarget->type == OBJ_DROID)
			{
				DROID	*psSpotter = (DROID *)psDroid->psTarget;

//				psFireTarget = orderStateObj((DROID *)psDroid->psTarget, DORDER_OBSERVE);
				if (psSpotter->action == DACTION_OBSERVE
				    || (psSpotter->droidType == DROID_COMMAND && psSpotter->action == DACTION_ATTACK))
				{
					psFireTarget = psSpotter->psActionTarget[0];
				}
			}
			else if (psDroid->psTarget->type == OBJ_STRUCTURE)
			{
				STRUCTURE *psSpotter = (STRUCTURE *)psDroid->psTarget;

				psFireTarget = psSpotter->psTarget[0];
			}

			if (psFireTarget && !psFireTarget->died)
			{
				bAttack = false;
				if (isVtolDroid(psDroid))
				{
					if (!vtolEmpty(psDroid) &&
						((psDroid->action == DACTION_MOVETOREARM) ||
						 (psDroid->action == DACTION_WAITFORREARM)) &&
						(psDroid->sMove.Status != MOVEINACTIVE) )
					{
						// catch vtols that were attacking another target which was destroyed
						// get them to attack the new target rather than returning to rearm
						bAttack = true;
					}
					else if (allVtolsRearmed(psDroid))
					{
						bAttack = true;
					}
				}
				else
				{
					bAttack = true;
				}

				//if not currently attacking or target has changed
				if ( bAttack &&
					(!droidAttacking(psDroid) ||
					 psFireTarget != psDroid->psActionTarget[0]))
				{
					//get the droid to attack
					actionDroidObj(psDroid, DACTION_ATTACK, psFireTarget);
				}
			}
			else if (isVtolDroid(psDroid) &&
					 (psDroid->action != DACTION_NONE) &&
					 (psDroid->action != DACTION_FIRESUPPORT))
			{
				moveToRearm(psDroid);
			}
			else if ((psDroid->action != DACTION_FIRESUPPORT) &&
					 (psDroid->action != DACTION_FIRESUPPORT_RETREAT))
			{
				actionDroidObj(psDroid, DACTION_FIRESUPPORT, psDroid->psTarget);
			}
		}
		break;
	case DORDER_RECYCLE:
		// don't bother with formations for this order
		if (psDroid->sMove.psFormation)
		{
			formationLeave(psDroid->sMove.psFormation, psDroid);
			psDroid->sMove.psFormation = NULL;
		}

		if (psDroid->psTarget == NULL)
		{
			psDroid->order = DORDER_NONE;
			actionDroid(psDroid, DACTION_NONE);
		}
		else if (actionReachedBuildPos(psDroid, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y,
						(BASE_STATS *)((STRUCTURE *)psDroid->psTarget)->pStructureType))
		{
			recycleDroid(psDroid);
		}
		else if (psDroid->action == DACTION_NONE)
		{
			actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
		}
		break;
	case DORDER_GUARD:
		if (orderDroidList(psDroid))
		{
			// started a queued order - quit
			break;
		}
		// HACK: we always want to update orders when NOT running a MP game,
		// and we don't want to update when the droid belongs to another human player
		else if (!myResponsibility(psDroid->player) && bMultiPlayer
					&& isHumanPlayer(psDroid->player))
		{
			return;
		}
		else if ((psDroid->action == DACTION_NONE) ||
				 (psDroid->action == DACTION_MOVE) ||
				 (psDroid->action == DACTION_MOVEFIRE))
		{
			// not doing anything, make sure the droid is close enough
			// to the thing it is defending
			//if ((psDroid->droidType != DROID_REPAIR) &&
            if ((!(psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
                DROID_CYBORG_REPAIR)) && psDroid->psTarget != NULL &&
                psDroid->psTarget->type == OBJ_DROID &&
				((DROID *)psDroid->psTarget)->droidType == DROID_COMMAND)
			{
				// guarding a commander, allow more space
				orderCheckGuardPosition(psDroid, DEFEND_CMD_BASEDIST);
			}
			else
			{
				orderCheckGuardPosition(psDroid, DEFEND_BASEDIST);
			}
		}
		else if (psDroid->droidType == DROID_REPAIR ||
            psDroid->droidType == DROID_CYBORG_REPAIR)
		{
			// repairing something, make sure the droid doesn't go too far
			orderCheckGuardPosition(psDroid, REPAIR_MAXDIST);
		}
		else if (psDroid->droidType == DROID_CONSTRUCT ||
            psDroid->droidType == DROID_CYBORG_CONSTRUCT)
		{
			// repairing something, make sure the droid doesn't go too far
			orderCheckGuardPosition(psDroid, CONSTRUCT_MAXDIST);
		}
        else if (psDroid->droidType == DROID_TRANSPORTER)
        {
            //check transporter isn't sitting there waiting to be filled when nothing exists!
            if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
                !missionDroidsRemaining(selectedPlayer))
            {
                //check that nothing is on the transporter (transporter counts as first in group)
                if (psDroid->psGroup && psDroid->psGroup->refCount < 2)
                {
    	    		//the script can call startMission for this callback for offworld missions
        			eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
                }
            }
        }
		else
		{
			//let vtols return to rearm
			if (!vtolRearming(psDroid))
			{
				// attacking something, make sure the droid doesn't go too far
				if (psDroid->psTarget != NULL && psDroid->psTarget->type == OBJ_DROID &&
					((DROID *)psDroid->psTarget)->droidType == DROID_COMMAND)
				{
					// guarding a commander, allow more space
					orderCheckGuardPosition(psDroid, DEFEND_CMD_MAXDIST);
				}
				else
				{
					orderCheckGuardPosition(psDroid, DEFEND_MAXDIST);
				}
			}
		}

		// get combat units in a command group to attack the commanders target
		if ( hasCommander(psDroid) && (psDroid->numWeaps > 0) )
		{
			if ((psDroid->psGroup->psCommander->action == DACTION_ATTACK) &&
				(psDroid->psGroup->psCommander->psActionTarget[0] != NULL) &&
				(!psDroid->psGroup->psCommander->psActionTarget[0]->died))
			{
				psObj = psDroid->psGroup->psCommander->psActionTarget[0];
				if (psDroid->action == DACTION_ATTACK ||
					psDroid->action == DACTION_MOVETOATTACK)
				{
					if (psDroid->psActionTarget[0] != psObj)
					{
						actionDroidObj(psDroid, DACTION_ATTACK, psObj);
					}
				}
				else if (psDroid->action != DACTION_MOVE)
				{
					actionDroidObj(psDroid, DACTION_ATTACK, psObj);
				}
			}

			// make sure units in a command group are actually guarding the commander
			psObj = orderStateObj(psDroid, DORDER_GUARD);	// find out who is being guarded by the droid
			if (psObj == NULL
			 || psObj != (BASE_OBJECT *)psDroid->psGroup->psCommander)
			{
				orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psDroid->psGroup->psCommander);
			}
		}

		//repair droids default to repairing droids within a given range
		psObj = NULL;
		if ((psDroid->droidType == DROID_REPAIR ||
            psDroid->droidType == DROID_CYBORG_REPAIR) &&
			secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
			(state != DSS_HALT_HOLD))
		{
			if (psDroid->action == DACTION_NONE)
			{
				psObj = checkForRepairRange(psDroid,NULL);
			}
			else if (psDroid->action == DACTION_SULK)
			{
				psObj = checkForRepairRange(psDroid,(DROID *)psDroid->psActionTarget[0]);
			}
			if (psObj)
			{
				actionDroidObj(psDroid, DACTION_DROIDREPAIR, (BASE_OBJECT *)psObj);
			}
		}
		//construct droids default to repairing structures within a given range
		psObj = NULL;
		if ((psDroid->droidType == DROID_CONSTRUCT ||
            psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
			secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
			(state != DSS_HALT_HOLD))
		{
			if (psDroid->action == DACTION_NONE)
			{
				psObj = checkForDamagedStruct(psDroid,NULL);
			}
			else if (psDroid->action == DACTION_SULK)
			{
				psObj = checkForDamagedStruct(psDroid,(STRUCTURE *)psDroid->psActionTarget);
			}
			if (psObj)
			{
				actionDroidObj(psDroid, DACTION_REPAIR, psObj);
			}
		}
		break;
	default:
		ASSERT( false, "orderUpdateUnit: unknown order" );
	}

	// catch any vtol that is rearming but has finished his order
	if (psDroid->order == DORDER_NONE && vtolRearming(psDroid)
	    && (psDroid->psActionTarget[0] == NULL || !psDroid->psActionTarget[0]->died))
	{
		psDroid->order = DORDER_REARM;
		setDroidTarget(psDroid, psDroid->psActionTarget[0]);
	}
}


/* Give a command group an order */
static void orderCmdGroupBase(DROID_GROUP *psGroup, DROID_ORDER_DATA *psData)
{
	DROID	*psCurr, *psChosen;
	SDWORD	xdiff,ydiff, currdist, mindist;

	ASSERT( psGroup != NULL,
		"cmdUnitOrderGroupBase: invalid unit group" );

	if (psData->order == DORDER_RECOVER)
	{
		// picking up an artifact - only need to send one unit
		psChosen = NULL;
		mindist = SDWORD_MAX;
		for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
		{
			xdiff = (SDWORD)psCurr->pos.x - (SDWORD)psData->psObj->pos.x;
			ydiff = (SDWORD)psCurr->pos.y - (SDWORD)psData->psObj->pos.y;
			currdist = xdiff*xdiff + ydiff*ydiff;
			if (currdist < mindist)
			{
				psChosen = psCurr;
				mindist = currdist;
			}
		}
		if (psChosen != NULL)
		{
			orderDroidBase(psChosen, psData);
		}
	}
	else
	{
		for (psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
		{
			if (!orderState(psCurr, DORDER_RTR))		// if you change this, youll need to change sendcmdgroup()
			{
				orderDroidBase(psCurr, psData);
			}
		}
	}
	turnOffMultiMsg(false);
}


// check the position of units giving fire support to this unit and tell
// them to pull back if the sensor is going to move through them
WZ_DECL_UNUSED static void orderCheckFireSupportPos(DROID *psSensor, DROID_ORDER_DATA *psOrder)
{
	SDWORD		fsx,fsy, fsnum, sensorVX,sensorVY, fsVX,fsVY;
	float		sensorAngle, fsAngle, adiff;
	SDWORD		xdiff,ydiff;
	SECONDARY_STATE state;
	DROID		*psCurr;
	BASE_OBJECT	*psTarget;
	BOOL		bRetreat;

	// find the middle of and droids doing firesupport
	fsx = fsy = fsnum = 0;
	for(psCurr=apsDroidLists[psSensor->player]; psCurr; psCurr=psCurr->psNext)
	{
		if (!isVtolDroid(psCurr)
		 && (psTarget = orderStateObj(psCurr, DORDER_FIRESUPPORT))
		 && psTarget == (BASE_OBJECT *)psSensor
		 && secondaryGetState(psCurr, DSO_HALTTYPE, &state)
		 && state != DSS_HALT_HOLD)
		{
			// got a unit doing fire support
			fsnum += 1;
			fsx += (SDWORD)psCurr->pos.x;
			fsy += (SDWORD)psCurr->pos.y;
		}
	}

	bRetreat = false;
	if (fsnum != 0)
	{
		// there are some units to check the position of
		fsx /= fsnum;
		fsy /= fsnum;

		// don't do it if too near to the firesupport units
		xdiff = fsx - (SDWORD)psSensor->pos.x;
		ydiff = fsy - (SDWORD)psSensor->pos.y;
		if (xdiff*xdiff + ydiff*ydiff < (TILE_UNITS*5)*(TILE_UNITS*5))
		{
			goto done;
		}

		sensorVX = (SDWORD)psOrder->x - (SDWORD)psSensor->pos.x;
		sensorVY = (SDWORD)psOrder->y - (SDWORD)psSensor->pos.y;
		fsVX = fsx - (SDWORD)psSensor->pos.x;
		fsVY = fsy - (SDWORD)psSensor->pos.y;

		// now check if the move position is further away than the firesupport units
		if (sensorVX*sensorVX + sensorVY*sensorVY < fsVX*fsVX + fsVY*fsVY)
		{
			goto done;
		}

		// now get the angle between the firesupport units and the sensor move
		sensorAngle = (float)atan2f(sensorVY, sensorVX);
		fsAngle = (float)atan2f(fsVY, fsVX);
		adiff = fsAngle - sensorAngle;
		if (adiff < 0)
		{
			adiff += (float)(M_PI * 2);
		}
		if (adiff > M_PI)
		{
			adiff -= (float)(M_PI);
		}

		// if the angle between the firesupport units and the sensor move is bigger
		// than 45 degrees don't retreat
		if (adiff > M_PI / 4)
		{
			goto done;
		}

		bRetreat = true;
	}

done:
	// made a decision whether to retreat

	// now move the firesupport units
	for(psCurr=apsDroidLists[psSensor->player]; psCurr; psCurr=psCurr->psNext)
	{
		if (!isVtolDroid(psCurr)
		 && (psTarget = orderStateObj(psCurr, DORDER_FIRESUPPORT))
		 && psTarget == (BASE_OBJECT *)psSensor
		 && secondaryGetState(psCurr, DSO_HALTTYPE, &state)
		 && state != DSS_HALT_HOLD)
		{
			if (bRetreat)
			{
				actionDroidLoc(psCurr, DACTION_FIRESUPPORT_RETREAT, psOrder->x, psOrder->y);
			}
			else if (psCurr->action == DACTION_FIRESUPPORT_RETREAT)
			{
				actionDroid(psCurr, DACTION_NONE);
			}
		}
	}
}



#define	AUDIO_DELAY_FIRESUPPORT		(3*GAME_TICKS_PER_SEC)

static void orderPlayFireSupportAudio( BASE_OBJECT *psObj )
{
	DROID		*psDroid = NULL;
	STRUCTURE	*psStruct = NULL;
	SDWORD		iAudioID = NO_SOUND;



	/* play appropriate speech */
	switch ( psObj->type )
	{
		case OBJ_DROID:
			psDroid = (DROID *) psObj;
			ASSERT( psObj != NULL,
					"orderPlayFireSupportAudio: invalid droid pointer" );
			if ( psDroid->droidType == DROID_COMMAND )
			{
				iAudioID = ID_SOUND_ASSIGNED_TO_COMMANDER;
			}
			else if ( psDroid->droidType == DROID_SENSOR )
			{
				iAudioID = ID_SOUND_ASSIGNED_TO_SENSOR;
			}
			break;

		case OBJ_STRUCTURE:
			ASSERT( psObj != NULL,
					"orderPlayFireSupportAudio: invalid structure pointer" );
			psStruct = (STRUCTURE *) psObj;
            //check for non-CB first
			if ( structStandardSensor(psStruct) || structVTOLSensor(psStruct) )
			{
				iAudioID = ID_SOUND_ASSIGNED_TO_SENSOR;
			}
			else if ( structCBSensor(psStruct) || structVTOLCBSensor(psStruct) )
			{
				iAudioID = ID_SOUND_ASSIGNED_TO_COUNTER_RADAR;
			}
			break;
		default:
			break;
	}

	if ( iAudioID != NO_SOUND )
	{
		audio_QueueTrackMinDelay( iAudioID, AUDIO_DELAY_FIRESUPPORT );
	}
}


/* The base order function */
void orderDroidBase(DROID *psDroid, DROID_ORDER_DATA *psOrder)
{
	UDWORD		iRepairFacDistSq, iStructDistSq, iFactoryDistSq;
	STRUCTURE	*psStruct, *psRepairFac, *psFactory;
	SDWORD		iDX, iDY;
	SECONDARY_STATE state;
	UDWORD		droidX,droidY;

	// deal with a droid receiving a primary order
	if (secondaryGotPrimaryOrder(psDroid, psOrder->order))
	{
		psOrder->order = DORDER_NONE;
	}

	// if this is a command droid - all it's units do the same thing
	if ((psDroid->droidType == DROID_COMMAND) &&
		(psDroid->psGroup != NULL) &&
		(psDroid->psGroup->type == GT_COMMAND) &&
		(psOrder->order != DORDER_GUARD) &&  //(psOrder->psObj == NULL)) &&
		(psOrder->order != DORDER_RTR) &&
		(psOrder->order != DORDER_RECYCLE) &&
		(psOrder->order != DORDER_MOVE))
	{
		if (psOrder->order == DORDER_ATTACK)
		{
			// change to attacktarget so that the group members
			// guard order does not get canceled
			psOrder->order = DORDER_ATTACKTARGET;
			orderCmdGroupBase(psDroid->psGroup, psOrder);
			psOrder->order = DORDER_ATTACK;
		}
		else
		{
			orderCmdGroupBase(psDroid->psGroup, psOrder);
		}

		// the commander doesn't have to pick up artifacts, one
		// of his units will do it for him (if there are any in his group).
		if ((psOrder->order == DORDER_RECOVER) &&
			(psDroid->psGroup->psList != NULL))
		{
			psOrder->order = DORDER_NONE;
		}
	}

	switch (psOrder->order)
	{
	case DORDER_NONE:
		// used when choose order cannot assign an order
		break;
	case DORDER_STOP:
		// get the droid to stop doing whatever it is doing
		actionDroid(psDroid, DACTION_NONE);
		psDroid->order = DORDER_NONE;
		setDroidTarget(psDroid, NULL);
		psDroid->psTarStats = NULL;
		psDroid->orderX = 0;
		psDroid->orderY = 0;
		psDroid->orderX2 = 0;
		psDroid->orderY2 = 0;
		break;
	case DORDER_MOVE:
	case DORDER_SCOUT:
		// can't move vtols to blocking tiles
		if (isVtolDroid(psDroid)
		 && (fpathGroundBlockingTile(map_coord(psOrder->x), map_coord(psOrder->y))
		  || !TEST_TILE_VISIBLE(psDroid->player, mapTile(map_coord(psOrder->x), map_coord(psOrder->y)))))
		{
			break;
		}
		//in multiPlayer, cannot move Transporter to blocking tile either
		if (game.maxPlayers > 0)
		{
			if (psDroid->droidType == DROID_TRANSPORTER
			 && (fpathGroundBlockingTile(map_coord(psOrder->x),
			                             map_coord(psOrder->y))
			  || !TEST_TILE_VISIBLE(psDroid->player,
			                        mapTile(map_coord(psOrder->x),
			                        map_coord(psOrder->y)))))
			{
				break;
			}
		}
		// move a droid to a location
		psDroid->order = psOrder->order;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
		break;
	case DORDER_PATROL:
		psDroid->order = psOrder->order;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		psDroid->orderX2 = psDroid->pos.x;
		psDroid->orderY2 = psDroid->pos.y;
		actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
		break;
	case DORDER_RECOVER:
		psDroid->order = DORDER_RECOVER;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x,psOrder->psObj->pos.y);
		break;
	case DORDER_TRANSPORTOUT:
		// tell a (transporter) droid to leave home base for the offworld mission
		psDroid->order = DORDER_TRANSPORTOUT;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		actionDroidLoc(psDroid, DACTION_TRANSPORTOUT, psOrder->x,psOrder->y);
		break;
	case DORDER_TRANSPORTRETURN:
		// tell a (transporter) droid to return after unloading
		psDroid->order = DORDER_TRANSPORTRETURN;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		actionDroidLoc(psDroid, DACTION_TRANSPORTOUT, psOrder->x,psOrder->y);
		break;
	case DORDER_TRANSPORTIN:
		// tell a (transporter) droid to fly onworld
		psDroid->order = DORDER_TRANSPORTIN;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		actionDroidLoc(psDroid, DACTION_TRANSPORTIN, psOrder->x,psOrder->y);
		break;
	case DORDER_ATTACK:
	case DORDER_ATTACKTARGET:
		if (psDroid->numWeaps == 0
		    || psDroid->asWeaps[0].nStat == 0
		    || psDroid->droidType == DROID_TRANSPORTER)
		{
			break;
		}
		else if (psDroid->order == DORDER_GUARD && psOrder->order == DORDER_ATTACKTARGET)
		{
			// attacking something while guarding, don't change the order
			actionDroidObj(psDroid, DACTION_ATTACK, (BASE_OBJECT *)psOrder->psObj);
		}
		else if (!psOrder->psObj->died)
		{
			//cannot attack a Transporter with EW in multiPlayer
			if (game.maxPlayers > 0 && electronicDroid(psDroid)
			    && psOrder->psObj->type == OBJ_DROID && ((DROID *)psOrder->psObj)->droidType == DROID_TRANSPORTER)
			{
				break;
			}
			setDroidTarget(psDroid, psOrder->psObj);
			psDroid->order = psOrder->order;

			if (isVtolDroid(psDroid)
			    || actionInsideMinRange(psDroid, psOrder->psObj, 0)
			    || (psOrder->order == DORDER_ATTACKTARGET
			        && secondaryGetState(psDroid, DSO_HALTTYPE, &state) && state == DSS_HALT_HOLD))
			{
				actionDroidObj(psDroid, DACTION_ATTACK, psOrder->psObj);
			}
			else
			{
				actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x, psOrder->psObj->pos.y);
			}
		}
		break;
	case DORDER_BUILD:
		// build a new structure
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		ASSERT( psOrder->psStats != NULL,
			"orderUnitBase: invalid structure stats pointer" );
		psDroid->order = DORDER_BUILD;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		setDroidTarget(psDroid, NULL);
		psDroid->psTarStats = psOrder->psStats;
		actionDroidLoc(psDroid, DACTION_BUILD, psOrder->x,psOrder->y);
		break;
	case DORDER_BUILDMODULE:
		//build a module onto the structure
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		psDroid->order = DORDER_BUILD;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, NULL);
		psDroid->psTarStats = (BASE_STATS *)getModuleStat((STRUCTURE *)psOrder->psObj);
		ASSERT(psDroid->psTarStats != NULL, "orderUnitBase: should have found a module stats");
		actionDroidLoc(psDroid, DACTION_BUILD, psOrder->psObj->pos.x,psOrder->psObj->pos.y);
		break;
	case DORDER_LINEBUILD:
		// build a line of structures
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		ASSERT(psOrder->psStats != NULL, "Invalid structure stats pointer");

		psDroid->order = DORDER_LINEBUILD;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		psDroid->orderX2 = psOrder->x2;
		psDroid->orderY2 = psOrder->y2;
		setDroidTarget(psDroid, NULL);
		psDroid->psTarStats = psOrder->psStats;
		actionDroidLoc(psDroid, DACTION_BUILD, psOrder->x,psOrder->y);
		break;
	case DORDER_HELPBUILD:
		// help to build a structure that is starting to be built
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		psDroid->order = DORDER_HELPBUILD;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		psDroid->psTarStats = (BASE_STATS *)((STRUCTURE *)psOrder->psObj)->pStructureType;
		actionDroidLoc(psDroid, DACTION_BUILD, psDroid->orderX, psDroid->orderY);
		break;
	case DORDER_DEMOLISH:
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		psDroid->order = DORDER_DEMOLISH;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_DEMOLISH, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_REPAIR:
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		psDroid->order = DORDER_REPAIR;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_REPAIR, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_DROIDREPAIR:
		if (!(psDroid->droidType == DROID_REPAIR || psDroid->droidType == DROID_CYBORG_REPAIR))
		{
			break;
		}
		psDroid->order = DORDER_DROIDREPAIR;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_DROIDREPAIR, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_OBSERVE:
		// keep an object within sensor view
		psDroid->order = DORDER_OBSERVE;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_OBSERVE, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_FIRESUPPORT:
		if (psDroid->asWeaps[0].nStat == 0)
		{
			break;
		}
		psDroid->order = DORDER_FIRESUPPORT;
		setDroidTarget(psDroid, psOrder->psObj);
		// let the order update deal with vtol droids
		if (!isVtolDroid(psDroid))
		{
			actionDroidObj(psDroid, DACTION_FIRESUPPORT, (BASE_OBJECT *)psOrder->psObj);
		}

		if ( psDroid->player == selectedPlayer )
		{
			orderPlayFireSupportAudio( psOrder->psObj );
		}
		break;
	case DORDER_RETREAT:
	case DORDER_RUNBURN:
	case DORDER_RUN:
		psDroid->order = psOrder->order;
		if ((psOrder->order == DORDER_RUN) &&
			((psOrder->x != 0) || (psOrder->y != 0)))
		{
			psDroid->orderX = psOrder->x;
			psDroid->orderY = psOrder->y;
		}
		else
		{
			psDroid->orderX = (UWORD)asRunData[psDroid->player].sPos.x;
			psDroid->orderY = (UWORD)asRunData[psDroid->player].sPos.y;
		}
		actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
		break;
	case DORDER_DESTRUCT:
		psDroid->order = DORDER_DESTRUCT;
		actionDroid(psDroid, DACTION_DESTRUCT);
		break;
	case DORDER_RTB:
		// send vtols back to their return pos
		if (isVtolDroid(psDroid) && !bMultiPlayer && psDroid->player != selectedPlayer)
		{
			iDX = asVTOLReturnPos[psDroid->player].x;
			iDY = asVTOLReturnPos[psDroid->player].y;
			if (iDX && iDY)
			{
				psDroid->order = DORDER_LEAVEMAP;
				actionDroidLoc(psDroid, DACTION_MOVE, iDX, iDY);
				if (psDroid->sMove.psFormation)
				{
					formationLeave(psDroid->sMove.psFormation, psDroid);
					psDroid->sMove.psFormation = NULL;
				}
				break;
			}
		}

		for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
		{
			if (psStruct->pStructureType->type == REF_HQ)
			{
				psDroid->order = DORDER_RTB;
				droidX = psStruct->pos.x;
				droidY = psStruct->pos.y;
				// Find a place to land for vtols. And Transporters in a multiPlay game.
				if (isVtolDroid(psDroid) || ((game.maxPlayers > 0) && (psDroid->droidType == DROID_TRANSPORTER)))
				{
					actionVTOLLandingPos(psDroid, &droidX,&droidY);
				}
				actionDroidLoc(psDroid, DACTION_MOVE, droidX,droidY);
				break;
			}
		}
		// no HQ go to the landing zone
		if (psDroid->order != DORDER_RTB)
		{
			// see if the LZ has been set up
			iDX = getLandingX(psDroid->player);
			iDY = getLandingY(psDroid->player);
			if (iDX && iDY)
			{
			    psDroid->order = DORDER_RTB;
			    //actionDroidLoc(psDroid, DACTION_MOVE, getLandingX(psDroid->player),
			    //				getLandingY(psDroid->player));
			    actionDroidLoc(psDroid, DACTION_MOVE, iDX,iDY);
			}
			else
			{
				// haven't got an LZ set up so don't do anything
				psDroid->order = DORDER_NONE;
			}
		}
		break;
	case DORDER_RTR:
	case DORDER_RTR_SPECIFIED:
		if (isVtolDroid(psDroid))
		{
			moveToRearm(psDroid);
			break;
		}
		if (psOrder->psObj == NULL)
		{
			psRepairFac = NULL;
			iRepairFacDistSq = 0;
			for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
			{
				if ((psStruct->pStructureType->type == REF_REPAIR_FACILITY) ||
					((psStruct->pStructureType->type == REF_HQ) && (psRepairFac == NULL)))
				{
					/* get droid->facility distance squared */
					iDX = (SDWORD)psDroid->pos.x - (SDWORD)psStruct->pos.x;
					iDY = (SDWORD)psDroid->pos.y - (SDWORD)psStruct->pos.y;
					iStructDistSq = iDX*iDX + iDY*iDY;

					/* choose current structure if first repair facility found or
					 * nearer than previously chosen facility
					 */
					if ( psRepairFac == NULL || (psRepairFac->pStructureType->type == REF_HQ) ||
						(iRepairFacDistSq > iStructDistSq) )
					{
						/* first facility found */
						psRepairFac = psStruct;
						iRepairFacDistSq = iStructDistSq;
					}
				}
			}
		}
		else
		{
			psRepairFac = (STRUCTURE *)psOrder->psObj;
		}

		// droids doing a DORDER_RTR periodically give themselves a DORDER_RTR so that
		// they always go to the nearest repair facility
		// this stops the unit doing anything more if the same repair facility gets chosen
		if (psDroid->order == DORDER_RTR &&
			psDroid->psTarget == (BASE_OBJECT *)psRepairFac)
		{
			break;
		}

		/* give repair order if repair facility found */
		if ( psRepairFac != NULL )
		{
			if (psRepairFac->pStructureType->type == REF_REPAIR_FACILITY)
			{
				/* move to front of structure */
				psDroid->order  = psOrder->order;
				psDroid->orderX = psRepairFac->pos.x;
				psDroid->orderY = psRepairFac->pos.y;
				setDroidTarget(psDroid, (BASE_OBJECT *) psRepairFac);
                /*if in multiPlayer, and the Transporter has been sent to be
                repaired, need to find a suitable location to drop down*/
                if (game.maxPlayers > 0 && psDroid->droidType == DROID_TRANSPORTER)
				{
                    UDWORD droidX, droidY;
                    droidX = psDroid->orderX;
                    droidY = psDroid->orderY;
					actionVTOLLandingPos(psDroid, &droidX,&droidY);
                    actionDroidLoc(psDroid, DACTION_MOVE, droidX,droidY);
				}
				else
                {
				    actionDroidObjLoc( psDroid, DACTION_MOVE, (BASE_OBJECT *) psRepairFac,
									psDroid->orderX, psDroid->orderY);
                }
			}
			else
			{
				orderDroid(psDroid, DORDER_RTB);
			}
		}
		else
		{
			// no repair facility or HQ go to the landing zone

			if (!bMultiPlayer && selectedPlayer == 0)

			{
				orderDroid(psDroid, DORDER_RTB);
			}
		}
		break;
	case DORDER_EMBARK:
		// move the droid to the transporter location
		psDroid->order = DORDER_EMBARK;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x, psOrder->psObj->pos.y);
		break;
	case DORDER_DISEMBARK:
        //only valid in multiPlayer mode - cannot use the check on bMultiPlayer since it has been
        //set to false before this function call
        if (game.maxPlayers > 0)
        {
            //this order can only be given to Transporter droids
            if (psDroid->droidType == DROID_TRANSPORTER)
            {
                psDroid->order = DORDER_DISEMBARK;
		        psDroid->orderX = psOrder->x;
		        psDroid->orderY = psOrder->y;
                //move the Transporter to the requested location
		        actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
                //close the Transporter interface - if up
                if (widgGetFromID(psWScreen,IDTRANS_FORM) != NULL)
                {
                    intRemoveTrans();
                }
            }
        }
        break;
	case DORDER_RECYCLE:
		psFactory = NULL;
		iFactoryDistSq = 0;
		for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
		{
            //look for nearest factory or repair facility
			if (psStruct->pStructureType->type == REF_FACTORY ||
				psStruct->pStructureType->type == REF_CYBORG_FACTORY ||
				psStruct->pStructureType->type == REF_VTOL_FACTORY ||
                psStruct->pStructureType->type == REF_REPAIR_FACILITY)
			{
				/* get droid->facility distance squared */
				iDX = (SDWORD)psDroid->pos.x - (SDWORD)psStruct->pos.x;
				iDY = (SDWORD)psDroid->pos.y - (SDWORD)psStruct->pos.y;
				iStructDistSq = iDX*iDX + iDY*iDY;

				/* choose current structure if first facility found or
				 * nearer than previously chosen facility
				 */
				if ( psFactory == NULL || (iFactoryDistSq > iStructDistSq) )
				{
					/* first facility found */
					psFactory = psStruct;
					iFactoryDistSq = iStructDistSq;
				}
			}
		}

		/* give recycle order if facility found */
		if ( psFactory != NULL )
		{
			/* move to front of structure */
			psDroid->order  = DORDER_RECYCLE;
			psDroid->orderX = psFactory->pos.x;
			psDroid->orderY = (UWORD)(psFactory->pos.y +
					world_coord(psFactory->pStructureType->baseBreadth) / 2 +
					TILE_UNITS / 2);
			setDroidTarget(psDroid, (BASE_OBJECT *) psFactory);
			actionDroidObjLoc( psDroid, DACTION_MOVE, (BASE_OBJECT *) psFactory,
								psDroid->orderX, psDroid->orderY);
		}

		break;
	case DORDER_GUARD:
		psDroid->order = DORDER_GUARD;
		setDroidTarget(psDroid, psOrder->psObj);
		if (psOrder->psObj != NULL)
		{
			psDroid->orderX = psOrder->psObj->pos.x;
			psDroid->orderY = psOrder->psObj->pos.y;
		}
		else
		{
			psDroid->orderX = psOrder->x;
			psDroid->orderY = psOrder->y;
		}
		actionDroid( psDroid, DACTION_NONE );
		break;
	case DORDER_RESTORE:
		if (!electronicDroid(psDroid))
		{
			break;
		}
		if (psOrder->psObj->type != OBJ_STRUCTURE)
		{
			ASSERT( false, "orderDroidBase: invalid object type for Restore order" );
			break;
		}
		psDroid->order = DORDER_RESTORE;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_RESTORE, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_CLEARWRECK:
		if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
		{
			break;
		}
		psDroid->order = DORDER_CLEARWRECK;
		psDroid->orderX = psOrder->psObj->pos.x;
		psDroid->orderY = psOrder->psObj->pos.y;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid, DACTION_CLEARWRECK, (BASE_OBJECT *)psOrder->psObj);
		break;
	case DORDER_REARM:
		// didn't get executed before
		if (!isVtolDroid(psDroid))
		{
			break;
		}
		psDroid->order = DORDER_REARM;
		setDroidTarget(psDroid, psOrder->psObj);
		actionDroidObj(psDroid,DACTION_MOVETOREARM, (BASE_OBJECT *)psOrder->psObj);
		assignVTOLPad(psDroid, (STRUCTURE *)psOrder->psObj);
		break;
	case DORDER_CIRCLE:
		if (!isVtolDroid(psDroid))
		{
			break;
		}
		psDroid->order = psOrder->order;
		psDroid->orderX = psOrder->x;
		psDroid->orderY = psOrder->y;
		actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
		break;
	default:
		ASSERT( false, "orderUnitBase: unknown order" );
		break;
	}
}


/* Give a droid an order */
void orderDroid(DROID *psDroid, DROID_ORDER order)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT( psDroid != NULL,
		"orderUnit: Invalid unit pointer" );
	ASSERT( order == DORDER_NONE ||
			order == DORDER_RETREAT ||
			order == DORDER_DESTRUCT ||
			order == DORDER_RTR ||
			order == DORDER_RTB ||
			order == DORDER_RECYCLE ||
			order == DORDER_RUN ||
			order == DORDER_RUNBURN ||
			order == DORDER_TRANSPORTIN ||
			order == DORDER_STOP,		// Added this PD.
		"orderUnit: Invalid order" );

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	orderDroidBase(psDroid, &sOrder);

	if(bMultiPlayer)
	{
		SendDroidInfo(psDroid,  order,  0,  0, NULL);
	}
}

/* Check the order state of a droid */
BOOL orderState(DROID *psDroid, DROID_ORDER order)
{
	if (order == DORDER_RTR)
	{
		return psDroid->order == DORDER_RTR || psDroid->order == DORDER_RTR_SPECIFIED;
	}

	return psDroid->order == order;
}

BOOL validOrderForLoc(DROID_ORDER order)
{
	return (order == DORDER_NONE ||	order == DORDER_MOVE ||	order == DORDER_GUARD ||
		order == DORDER_SCOUT || order == DORDER_RUN || order == DORDER_PATROL ||
		order == DORDER_TRANSPORTOUT || order == DORDER_TRANSPORTIN  ||
		order == DORDER_TRANSPORTRETURN || order == DORDER_DISEMBARK ||
		order == DORDER_CIRCLE);
}

/* Give a droid an order with a location target */
void orderDroidLoc(DROID *psDroid, DROID_ORDER order, UDWORD x, UDWORD y)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL, "Invalid unit pointer");
	ASSERT(validOrderForLoc(order), "Invalid order for location");

	orderClearDroidList(psDroid);

	if(bMultiPlayer) //ajl
	{
		SendDroidInfo(psDroid,  order,  x,  y, NULL);
		turnOffMultiMsg(true);	// msgs off.
	}

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x;
	sOrder.y = (UWORD)y;
	orderDroidBase(psDroid, &sOrder);

	turnOffMultiMsg(false);	//msgs back on..
}


/* Get the state of a droid order with it's location */
BOOL orderStateLoc(DROID *psDroid, DROID_ORDER order, UDWORD *pX, UDWORD *pY)
{
	if (order != psDroid->order)
	{
		return false;
	}

	// check the order is one with a location
	switch (psDroid->order)
	{
	default:
		// not a location order - return false
		break;
	case DORDER_MOVE:
	case DORDER_RETREAT:
		*pX = psDroid->orderX;
		*pY = psDroid->orderY;
		return true;
		break;
	}

	return false;
}

BOOL validOrderForObj(DROID_ORDER order)
{
	return (order == DORDER_NONE || order == DORDER_HELPBUILD || order == DORDER_DEMOLISH ||
		order == DORDER_REPAIR || order == DORDER_ATTACK || order == DORDER_FIRESUPPORT ||
		order == DORDER_OBSERVE || order == DORDER_ATTACKTARGET || order == DORDER_RTR ||
		order == DORDER_RTR_SPECIFIED || order == DORDER_EMBARK || order == DORDER_GUARD ||
		order == DORDER_DROIDREPAIR || order == DORDER_RESTORE || order == DORDER_BUILDMODULE ||
		order == DORDER_REARM || order == DORDER_CLEARWRECK || order == DORDER_RECOVER);
}

/* Give a droid an order with an object target */
void orderDroidObj(DROID *psDroid, DROID_ORDER order, BASE_OBJECT *psObj)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL, "Invalid unit pointer");
	ASSERT(validOrderForObj(order), "Invalid order for object");

	orderClearDroidList(psDroid);

	if(bMultiPlayer) //ajl
	{
		SendDroidInfo(psDroid, order, 0, 0, psObj);
	}

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.psObj = psObj;
	orderDroidBase(psDroid, &sOrder);
}


/* Get the state of a droid order with an object */
BASE_OBJECT* orderStateObj(DROID *psDroid, DROID_ORDER order)
{
	BOOL	match = false;

	switch (order)
	{
	case DORDER_BUILD:
	case DORDER_LINEBUILD:
	case DORDER_HELPBUILD:
		if (psDroid->order == DORDER_BUILD ||
			psDroid->order == DORDER_HELPBUILD ||
			psDroid->order == DORDER_LINEBUILD)
		{
			match = true;
		}
		break;
	case DORDER_ATTACK:
	case DORDER_FIRESUPPORT:
	case DORDER_OBSERVE:
	case DORDER_DEMOLISH:
	case DORDER_DROIDREPAIR:
	case DORDER_REARM:
	case DORDER_GUARD:
		if (psDroid->order == order)
		{
			match = true;
		}
		break;
	case DORDER_RTR:
		if (psDroid->order == DORDER_RTR ||
			psDroid->order == DORDER_RTR_SPECIFIED)
		{
			match = true;
		}
	default:
		break;
	}

	if (!match)
	{
		return NULL;
	}

	// check the order is one with an object
	switch (psDroid->order)
	{
	default:
		// not an object order - return false
		return NULL;
		break;
	case DORDER_BUILD:
	case DORDER_LINEBUILD:
		if (psDroid->action == DACTION_BUILD ||
			psDroid->action == DACTION_BUILDWANDER)
		{
			return psDroid->psTarget;
		}
		break;
	case DORDER_HELPBUILD:
		if (psDroid->action == DACTION_BUILD ||
			psDroid->action == DACTION_BUILDWANDER ||
			psDroid->action == DACTION_MOVETOBUILD)
		{
			return psDroid->psTarget;
		}
		break;
	//case DORDER_HELPBUILD:
	case DORDER_ATTACK:
	case DORDER_FIRESUPPORT:
	case DORDER_OBSERVE:
	case DORDER_DEMOLISH:
	case DORDER_RTR:
	case DORDER_RTR_SPECIFIED:
	case DORDER_DROIDREPAIR:
	case DORDER_REARM:
	case DORDER_GUARD:
		return psDroid->psTarget;
		break;
	}

	return NULL;
}


/* Give a droid an order with a location and a stat */
void orderDroidStatsLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x, UDWORD y)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL, "Invalid unit pointer");
	ASSERT(order == DORDER_BUILD, "Invalid order for location");

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x;
	sOrder.y = (UWORD)y;
	sOrder.psStats = psStats;
	orderDroidBase(psDroid, &sOrder);
}

/* add an order with a location and a stat to the droids order list*/
void orderDroidStatsLocAdd(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x, UDWORD y)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL, "Invalid unit pointer");

	// can only queue build orders with this function
	if (order != DORDER_BUILD)
	{
		return;
	}

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x;
	sOrder.y = (UWORD)y;
	sOrder.psStats = psStats;
	orderDroidAdd(psDroid, &sOrder);
}


/* Give a droid an order with a location and a stat */
void orderDroidStatsTwoLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL,	"Invalid unit pointer");
	ASSERT(order == DORDER_LINEBUILD, "Invalid order for location");
	ASSERT(x1 == x2 || y1 == y2, "Invalid locations for LINEBUILD");

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x1;
	sOrder.y = (UWORD)y1;
	sOrder.x2 = (UWORD)x2;
	sOrder.y2 = (UWORD)y2;
	sOrder.psStats = psStats;
	orderDroidBase(psDroid, &sOrder);
}

/* Add an order with a location and a stat */
void orderDroidStatsTwoLocAdd(DROID *psDroid, DROID_ORDER order,
						BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2)
{
	DROID_ORDER_DATA	sOrder;

	ASSERT(psDroid != NULL, "Invalid unit pointer");
	ASSERT(order == DORDER_LINEBUILD, "Invalid order for location");
	ASSERT(x1 == x2 || y1 == y2, "Invalid locations for LINEBUILD");

	memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x1;
	sOrder.y = (UWORD)y1;
	sOrder.x2 = (UWORD)x2;
	sOrder.y2 = (UWORD)y2;
	sOrder.psStats = psStats;
	orderDroidAdd(psDroid, &sOrder);
}


/* Get the state of a droid order with a location and a stat */
BOOL orderStateStatsLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS **ppsStats, UDWORD *pX, UDWORD *pY)
{
	BOOL	match = false;

	switch (order)
	{
	case DORDER_BUILD:
	case DORDER_LINEBUILD:
		if (psDroid->order == DORDER_BUILD ||
			psDroid->order == DORDER_LINEBUILD)
		{
			match = true;
		}
		break;
	default:
		break;
	}
	if (!match)
	{
		return false;
	}

	// check the order is one with stats and a location
	switch (psDroid->order)
	{
	default:
		// not a stats/location order - return false
		return false;
		break;
	case DORDER_BUILD:
	case DORDER_LINEBUILD:
		if (psDroid->action == DACTION_MOVETOBUILD ||
			psDroid->action == DACTION_BUILD_FOUNDATION ||
			psDroid->action == DACTION_FOUNDATION_WANDER)
		{
			*ppsStats = psDroid->psTarStats;
			*pX = psDroid->orderX;
			*pY = psDroid->orderY;
			return true;
		}
		break;
	}

	return false;
}

// add an order to a droids order list
void orderDroidAdd(DROID *psDroid, DROID_ORDER_DATA *psOrder)
{
	Vector3i position;

	ASSERT(psDroid != NULL, "Invalid unit pointer");

	if (psDroid->listSize >= ORDER_LIST_MAX)
	{
		// no room to store the order, quit
		return;
	}

	// if not doing anything - do it immediately
	if (psDroid->listSize == 0 &&
		(psDroid->order == DORDER_NONE ||
		 psDroid->order == DORDER_GUARD))
	{
		orderDroidBase(psDroid, psOrder);
	}
	else
	{
		psDroid->asOrderList[psDroid->listSize].order = psOrder->order;
		//psDroid->asOrderList[psDroid->listSize].psObj = psOrder->psObj;
        if (psOrder->order == DORDER_BUILD || psOrder->order == DORDER_LINEBUILD)
        {
		setDroidOrderTarget(psDroid, psOrder->psStats, psDroid->listSize);
	}
        else
        {
		setDroidOrderTarget(psDroid, psOrder->psObj, psDroid->listSize);
        }
		psDroid->asOrderList[psDroid->listSize].x = (UWORD)psOrder->x;
		psDroid->asOrderList[psDroid->listSize].y = (UWORD)psOrder->y;
		psDroid->asOrderList[psDroid->listSize].x2 = (UWORD)psOrder->x2;
		psDroid->asOrderList[psDroid->listSize].y2 = (UWORD)psOrder->y2;
		psDroid->listSize += 1;
	}

    //don't display the arrow-effects with build orders since unnecessary
	if (!bOrderEffectDisplayed && (psOrder->order != DORDER_BUILD ||
        psOrder->order != DORDER_LINEBUILD || psOrder->order !=
        DORDER_BUILDMODULE || psOrder->order != DORDER_HELPBUILD))
	{
		position.x = psOrder->x;
		position.z = psOrder->y;
		position.y = map_Height(position.x, position.z) + 32;
		if ((psOrder->psObj != NULL) &&
			(psOrder->psObj->sDisplay.imd != NULL))
		{
			position.y += psOrder->psObj->sDisplay.imd->max.y;
		}
		addEffect(&position,EFFECT_WAYPOINT,WAYPOINT_TYPE,false,NULL,0);
		bOrderEffectDisplayed = true;
	}
}


// do the next order from a droids order list
BOOL orderDroidList(DROID *psDroid)
{
	DROID_ORDER_DATA	sOrder;

	if (psDroid->listSize > 0)
	{
		// there are some orders to give
		memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
		sOrder.order = psDroid->asOrderList[0].order;
        //sOrder.psObj = psDroid->asOrderList[0].psObj;
        switch (psDroid->asOrderList[0].order)
        {
        case DORDER_MOVE:
		sOrder.psObj = NULL;
		break;
        case DORDER_ATTACK:
        case DORDER_REPAIR:
        case DORDER_OBSERVE:
        case DORDER_DROIDREPAIR:
        case DORDER_FIRESUPPORT:
        case DORDER_CLEARWRECK:
        case DORDER_DEMOLISH:
        case DORDER_HELPBUILD:
        case DORDER_BUILDMODULE:
            sOrder.psObj = (BASE_OBJECT *)psDroid->asOrderList[0].psOrderTarget;
            break;
        case DORDER_BUILD:
        case DORDER_LINEBUILD:
            sOrder.psObj = NULL;
            sOrder.psStats = (BASE_STATS *)psDroid->asOrderList[0].psOrderTarget;
            break;
        default:
            ASSERT( false, "orderDroidList: Invalid order" );
            return false;
        }
		sOrder.x	 = psDroid->asOrderList[0].x;
		sOrder.y	 = psDroid->asOrderList[0].y;
		sOrder.x2	 = psDroid->asOrderList[0].x2;
		sOrder.y2	 = psDroid->asOrderList[0].y2;
		psDroid->listSize -= 1;

		// move the rest of the list down
		memmove(psDroid->asOrderList, psDroid->asOrderList + 1, psDroid->listSize * sizeof(ORDER_LIST));
		memset(psDroid->asOrderList + psDroid->listSize, 0, sizeof(ORDER_LIST));

		orderDroidBase(psDroid, &sOrder);

        //don't send BUILD orders in multiplayer
		if(bMultiPlayer && !(sOrder.order == DORDER_BUILD || sOrder.order == DORDER_LINEBUILD))
		{
			SendDroidInfo(psDroid,  sOrder.order , sOrder.x, sOrder.y,sOrder.psObj);
		}

		return true;
	}

	return false;
}


// clear all the orders from the list
void orderClearDroidList(DROID *psDroid)
{
	psDroid->listSize = 0;
	memset(psDroid->asOrderList, 0, sizeof(ORDER_LIST)*ORDER_LIST_MAX);
}

// check all the orders in the list for died objects
void orderCheckList(DROID *psDroid)
{
	SDWORD	i;

	i=0;
	while (i<psDroid->listSize)
	{
        //if (psDroid->asOrderList[i].psObj &&
	    //	(psDroid->asOrderList[i].psObj)->died)

        //if order requires an object
        if (psDroid->asOrderList[i].order == DORDER_ATTACK ||
            psDroid->asOrderList[i].order == DORDER_REPAIR ||
            psDroid->asOrderList[i].order == DORDER_OBSERVE ||
            psDroid->asOrderList[i].order == DORDER_DROIDREPAIR ||
            psDroid->asOrderList[i].order == DORDER_FIRESUPPORT ||
            psDroid->asOrderList[i].order == DORDER_CLEARWRECK ||
            psDroid->asOrderList[i].order == DORDER_DEMOLISH ||
            psDroid->asOrderList[i].order == DORDER_HELPBUILD ||
            psDroid->asOrderList[i].order == DORDER_BUILDMODULE)
        {
    		if ((BASE_OBJECT *)psDroid->asOrderList[i].psOrderTarget &&
	    		((BASE_OBJECT *)psDroid->asOrderList[i].psOrderTarget)->died)
		    {
			    // copy any other orders down the stack
			    psDroid->listSize -= 1;
			    memmove(psDroid->asOrderList + i, psDroid->asOrderList + i + 1,
                    (psDroid->listSize - i) * sizeof(ORDER_LIST));
			    memset(psDroid->asOrderList + psDroid->listSize, 0, sizeof(ORDER_LIST));
            }
            else
            {
                i++;
            }
		}
		else
		{
			i ++;
		}
	}

}


// add a location order to a droids order list
static BOOL orderDroidLocAdd(DROID *psDroid, DROID_ORDER order, UDWORD x, UDWORD y)
{
	DROID_ORDER_DATA	sOrder;

	// can only queue move orders
	if (order != DORDER_MOVE)
	{
		return false;
	}

	memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.x = (UWORD)x;
	sOrder.y = (UWORD)y;
	orderDroidAdd(psDroid, &sOrder);

	return true;
}


// add an object order to a droids order list
static BOOL orderDroidObjAdd(DROID *psDroid, DROID_ORDER order, BASE_OBJECT *psObj[DROID_MAXWEAPS])
{
	DROID_ORDER_DATA	sOrder;

	// check can queue the order
	if (order != DORDER_ATTACK &&
		order != DORDER_REPAIR &&
		order != DORDER_OBSERVE &&
		order != DORDER_DROIDREPAIR &&
		order != DORDER_FIRESUPPORT &&
		order != DORDER_CLEARWRECK &&
        order != DORDER_DEMOLISH &&
        order != DORDER_HELPBUILD &&
        order != DORDER_BUILDMODULE)
	{
		return false;
	}

	memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
	sOrder.order = order;
	sOrder.psObj = psObj[0];
	sOrder.x = (UWORD)psObj[0]->pos.x;
	sOrder.y = (UWORD)psObj[0]->pos.y;
	orderDroidAdd(psDroid, &sOrder);

	return true;
}

/* Choose an order for a droid from a location */
DROID_ORDER chooseOrderLoc(DROID *psDroid, UDWORD x,UDWORD y)
{
	DROID_ORDER		order;
	SECONDARY_STATE			state;

	// default to move
	order = DORDER_MOVE;

	// scout if shift was pressed
	if(keyDown(KEY_LSHIFT) || keyDown(KEY_RSHIFT))
	{
		order = DORDER_SCOUT;
	}

	/*if(psDroid->droidType == DROID_TRANSPORTER)
	{
		order = DORDER_NONE;
	}*/
	//VTOL Droids won't move to a location - only to a target object -
	//this cope with Transporters as well - not any more! AB 16/11/98
//	if (isVtolDroid(psDroid) || psDroid->droidType == DROID_TRANSPORTER)

    //and now we want Transporters to fly! - in multiPlayer!!
    if (psDroid->droidType == DROID_TRANSPORTER)
	{
        //if (!bMultiPlayer)
        if (game.maxPlayers == 0)
        {
		    order = DORDER_NONE;
        }
        /*in MultiPlayer - if ALT-key is pressed then need to get the Transporter
        to fly to location and all units disembark*/
        else if (keyDown(KEY_LALT) || keyDown(KEY_RALT))
        {
            order = DORDER_DISEMBARK;
        }
	}
	else if (secondaryGetState(psDroid, DSO_CIRCLE, &state) &&
		state == DSS_CIRCLE_SET)
	{
		order = DORDER_CIRCLE;
		secondarySetState(psDroid, DSO_CIRCLE, DSS_NONE);
	}
	else if (secondaryGetState(psDroid, DSO_PATROL, &state) &&
		state == DSS_PATROL_SET)
	{
		order = DORDER_PATROL;
		secondarySetState(psDroid, DSO_PATROL, DSS_NONE);
	}

	return order;
}


/* Give selected droids an order from a location target or
   move selected Delivery Point to new location

   If add is true then the order is queued in the droid
*/
void orderSelectedLocAdd(UDWORD player, UDWORD x, UDWORD y, BOOL add)
{
	DROID			*psCurr;//, *psPrev;
	DROID_ORDER		order;
//	FORMATION		*psFormation = NULL;

//	DBPRINTF(("orderSelectedLoc: player %d -> (%d,%d)\n", player, x,y));

	//if were in build select mode ignore all other clicking
	if (intBuildSelectMode())
	{
		return;
	}


	if (!add && bMultiPlayer && SendGroupOrderSelected((UBYTE)player,x,y,NULL) )
	{	// turn off multiplay messages,since we've send a group one instead.
		turnOffMultiMsg(true);
	}


	// remove any units from their command group
	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected && hasCommander(psCurr))
		{
			grpLeave(psCurr->psGroup, psCurr);
		}
	}

	// note that an order list graphic needs to be displayed
	bOrderEffectDisplayed = false;

	//	psPrev = NULL;
	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected)
		{
			order = chooseOrderLoc(psCurr, x,y);
			// see if the order can be added to the list
			if (!(add && orderDroidLocAdd(psCurr, order, x,y)))
			{
				// if not just do it straight off
				orderDroidLoc(psCurr, order, x,y);
			}
		}
	}

	//might be a Delivery Point of a factory
	//processDeliveryPoint(player,x,y);

	turnOffMultiMsg(false);	// msgs back on...
}


void orderSelectedLoc(UDWORD player, UDWORD x, UDWORD y)
{
	orderSelectedLocAdd(player, x,y, false);
}


/* Choose an order for a droid from an object */
DROID_ORDER chooseOrderObj(DROID *psDroid, BASE_OBJECT *psObj)
{
	DROID_ORDER		order;
	STRUCTURE		*psStruct;
	FEATURE			*psFeature;

	if(psDroid->droidType == DROID_TRANSPORTER)
	{
        //in multiPlayer, need to be able to get Transporter repaired
        if (bMultiPlayer)
        {
            //default to no order
            order = DORDER_NONE;
            if (psObj->player == psDroid->player &&
			  psObj->type == OBJ_STRUCTURE)
            {
		        psStruct = (STRUCTURE *) psObj;
		        ASSERT( psObj != NULL,
				        "chooseOrderObj: invalid structure pointer" );
			    if ( psStruct->pStructureType->type == REF_REPAIR_FACILITY &&
				     psStruct->status == SS_BUILT)
			    {
				    order = DORDER_RTR_SPECIFIED;
			    }
            }
            return (order);
        }
        else
        {
		    return(DORDER_NONE);
        }
	}

	//check for transporters first
	if (psObj->type == OBJ_DROID && ((DROID *)psObj)->droidType == DROID_TRANSPORTER
		&& psObj->player == psDroid->player)
	{
		order = DORDER_EMBARK;

        //Cannot test this here since bMultiPlayer will have been set to false
/*        //in multiPlayer can only put cyborgs onto a Transporter
        if (bMultiPlayer && psDroid->droidType != DROID_CYBORG)
        {
            order = DORDER_NONE;
        }
*/
	}
	// go to recover an artifact/oil drum - don't allow VTOL's to get this order
	else if (psObj->type == OBJ_FEATURE &&
		(((FEATURE *)psObj)->psStats->subType == FEAT_GEN_ARTE ||
		 ((FEATURE *)psObj)->psStats->subType == FEAT_OIL_DRUM) )
	{
        if (isVtolDroid(psDroid))
        {
            order = DORDER_NONE;
        }
        else
        {
		    order = DORDER_RECOVER;
        }
	}
	// else default to attack if the droid has a weapon
	else if (psDroid->numWeaps > 0
			&& psObj->player != psDroid->player
			&& !aiCheckAlliances(psObj->player , psDroid->player) )
	{
        //check valid weapon/prop combination
        if (!validTarget((BASE_OBJECT *)psDroid, psObj, 0))
        {
            order = DORDER_NONE;
        }
		else
		{
			order = DORDER_ATTACK;
		}
	}
	else if (psDroid->droidType == DROID_SENSOR
			&& psObj->player != psDroid->player
			&& !aiCheckAlliances(psObj->player , psDroid->player) )
	{
		//check for standard sensor or VTOL intercept sensor
		if (asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == STANDARD_SENSOR ||
			asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == VTOL_INTERCEPT_SENSOR ||
            asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == SUPER_SENSOR)
		{
 			// a sensor droid observing an object
			order = DORDER_OBSERVE;
		}
		else
		{
			order = DORDER_NONE;
		}
	}
	else if (droidSensorDroidWeapon(psObj, psDroid))
	{
		// got an indirect weapon droid or vtol doing fire support
		order = DORDER_FIRESUPPORT;
		setSensorAssigned();
	}
	else if ( psObj->player == psDroid->player &&
			  psObj->type == OBJ_DROID &&
			  ((DROID *)psObj)->droidType == DROID_COMMAND &&
			  psDroid->droidType != DROID_COMMAND &&
			  psDroid->droidType != DROID_CONSTRUCT &&
              psDroid->droidType != DROID_CYBORG_CONSTRUCT)
	{
//		if (!isVtolDroid(psDroid))
		{
			// get a droid to join a command droids group
			cmdDroidAddDroid((DROID *) psObj, psDroid);
			DeSelectDroid(psDroid);

			order = DORDER_NONE;
		}
/*		else
		{
			order = DORDER_FIRESUPPORT;
		}*/
	}
	//repair droid
	else if (psObj->player == psDroid->player &&
		psObj->type == OBJ_DROID &&
		//psDroid->droidType == DROID_REPAIR &&
        (psDroid->droidType == DROID_REPAIR ||
        psDroid->droidType == DROID_CYBORG_REPAIR) &&
		droidIsDamaged((DROID *)psObj))
	{
		order = DORDER_DROIDREPAIR;
	}
	// guarding constructor droids
	else if (psObj->player == psDroid->player &&
			 psObj->type == OBJ_DROID &&
			 (((DROID *)psObj)->droidType == DROID_CONSTRUCT ||
             ((DROID *)psObj)->droidType == DROID_CYBORG_CONSTRUCT ||
             ((DROID *)psObj)->droidType == DROID_SENSOR) &&
			 (psDroid->droidType == DROID_WEAPON ||
             psDroid->droidType == DROID_CYBORG) &&
			 proj_Direct(asWeaponStats + psDroid->asWeaps[0].nStat))
	{
		order = DORDER_GUARD;
		assignSensorTarget(psObj);
		psDroid->selected = false;
	}
	else if ( psObj->player == psDroid->player &&
			  psObj->type == OBJ_STRUCTURE )
	{
		psStruct = (STRUCTURE *) psObj;
		ASSERT( psObj != NULL,
				"chooseOrderObj: invalid structure pointer" );

		/* check whether construction droid */
		order = DORDER_NONE;
		if ( psDroid->droidType == DROID_CONSTRUCT ||
            psDroid->droidType == DROID_CYBORG_CONSTRUCT)
		{
            //Re-written to allow demolish order to be added to the queuing system
            if (intDemolishSelectMode())
			{
				//check to see if anything is currently trying to build the structure
				//can't build and demolish at the same time!
				if (psStruct->status != SS_BUILT &&
					checkDroidsBuilding(psStruct))
				{
					order = DORDER_NONE;
					psDroid->psTarStats = NULL;
				}
				else
				{
					order = DORDER_DEMOLISH;
				}
			}
			//check for non complete structures
			else if (psStruct->status != SS_BUILT)
			{
				//if something else is demolishing, then help demolish
				if (checkDroidsDemolishing(psStruct))
				{
					psDroid->psTarStats = (BASE_STATS *) structGetDemolishStat();
					order = DORDER_DEMOLISH;
				}
				//else help build
				else
				{
					order = DORDER_HELPBUILD;
				}
			}
			//check for half built structure
			/*else if ( psStruct->status == SS_BEING_BUILT)
			{
				// got a construction droid building a structure
				order = DORDER_HELPBUILD;
			}*/
			//else if ( psStruct->body < psStruct->baseBodyPoints )
			else if ( psStruct->body < structureBody(psStruct))
			{
				order = DORDER_REPAIR;
			}
			//check if can build a module
			else if (buildModule(psStruct))
			{
				order = DORDER_BUILDMODULE;
			}
			else
			{
				order = DORDER_NONE;
			}
		}

		if (order == DORDER_NONE)
		{
			/* check repair facility and in need of repair */
			if ( psStruct->pStructureType->type == REF_REPAIR_FACILITY &&
				 psStruct->status == SS_BUILT)
	//			((SDWORD)(PERCENT(psDroid->body,psDroid->originalBody)) < 100) )
			{
				order = DORDER_RTR_SPECIFIED;
			}
			else if (electronicDroid(psDroid) &&
				//psStruct->resistance < (SDWORD)(psStruct->pStructureType->resistance))
				psStruct->resistance < (SDWORD)structureResistance(psStruct->
					pStructureType, psStruct->player))
			{
				order = DORDER_RESTORE;
			}
			//check for counter battery assignment
			else if (structSensorDroidWeapon(psStruct, psDroid))
			{
//				secondarySetState(psDroid, DSO_HALTTYPE, DSS_HALT_HOLD);
				order = DORDER_FIRESUPPORT;
				//inform display system
				setSensorAssigned();
				//deselect droid
	//			psDroid->selected = false;
				DeSelectDroid(psDroid);
			}
			//REARM VTOLS
			else if (isVtolDroid(psDroid))
			{
				//default to no order
				order = DORDER_NONE;
				//check if rearm pad
				if (psStruct->pStructureType->type == REF_REARM_PAD)
				{
					//don't bother checking cos we want it to go there if directed
					//check if need to be rearmed/repaired
					//if (!vtolHappy(psDroid))
					{
						order = DORDER_REARM;
					}
				}
			}
			else
			{
				order = DORDER_GUARD;
			}
		}
	}
	//check for constructor droid clearing up wrecked buildings
	else if ( (psDroid->droidType == DROID_CONSTRUCT ||
        psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
        psObj->type == OBJ_FEATURE )
	{
		psFeature = (FEATURE *) psObj;
		ASSERT( psObj != NULL,
				"chooseOrderObj: invalid feature pointer" );
		if (psFeature->psStats->subType == FEAT_BUILD_WRECK)
		{
			order = DORDER_CLEARWRECK;
		}
		else
		{
			order = DORDER_NONE;
		}
	}
	else
	{
		order = DORDER_NONE;
	}

	return order;
}

static void orderPlayOrderObjAudio( UDWORD player, BASE_OBJECT *psObj )
{
	DROID	*psDroid;

	/* loop over selected droids */
	for( psDroid = apsDroidLists[player]; psDroid; psDroid=psDroid->psNext )
	{
		if ( psDroid->selected )
		{
			/* currently only looks for VTOL */
			if ( isVtolDroid( psDroid ) )
			{
				switch ( psDroid->order )
				{
					case DORDER_ATTACK:
						audio_QueueTrack( ID_SOUND_ON_OUR_WAY2 );
						break;
				}
			}

			/* only play audio once */
			break;
		}
	}
}

/* Give selected droids an order from an object target
 * If add is true the order is queued with the droid
 */
void orderSelectedObjAdd(UDWORD player, BASE_OBJECT *psObj, BOOL add)
{
	DROID		*psCurr, *psDemolish;
	DROID_ORDER	order;

	if (!add && bMultiPlayer && SendGroupOrderSelected((UBYTE)player,0,0,psObj) )
	{	// turn off multiplay messages,since we've send a group one instead.
		turnOffMultiMsg(true);
	}


	// remove any units from their command group
	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected && hasCommander(psCurr))
		{
			grpLeave(psCurr->psGroup, psCurr);
		}
	}

	// note that an order list graphic needs to be displayed
	bOrderEffectDisplayed = false;

    psDemolish = NULL;
    for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected)
		{
			order = chooseOrderObj(psCurr, psObj);
            if (order == DORDER_DEMOLISH && player == selectedPlayer)
            {
                psDemolish = psCurr;
            }
			// see if the order can be added to the list
			if (!(add && orderDroidObjAdd(psCurr, order, &psObj)))
			{
				// if not just do it straight off
				orderDroidObj(psCurr, order, psObj);
			}
		}
	}

	orderPlayOrderObjAudio( player, psObj );

	turnOffMultiMsg(false); //msgs back on.

    //This feels like the wrong place but it has to be done once the order has been received...
    //demolish queuing...need to bring the interface back up
    if (psDemolish)
    {
        /*this will stop the constructor being able to demolish any other
        buildings until the demolish button is re-selected*/
        intDemolishCancel();

//turn off the build queue availability until desired release date!
#ifndef DISABLE_BUILD_QUEUE
        //re-add the stat (side) interface to allow a new selection
        if (ctrlShiftDown())
        {
            intConstructorSelected(psDemolish);
        }
#endif
    }

}

void orderSelectedObj(UDWORD player, BASE_OBJECT *psObj)
{
	orderSelectedObjAdd(player, psObj, false);
}


/* order all selected droids with a location and a stat */
void orderSelectedStatsLoc(UDWORD player, DROID_ORDER order,
						   BASE_STATS *psStats, UDWORD x, UDWORD y, BOOL add)
{
	DROID		*psCurr;

//turn off the build queue availability until desired release date!
#ifdef DISABLE_BUILD_QUEUE
    add = false;
#endif

    for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected)
		{
            if (add)
            {
                orderDroidStatsLocAdd(psCurr, order, psStats, x,y);
            }
            else
            {
			    orderDroidStatsLoc(psCurr, order, psStats, x,y);
            }
		}
	}
}


/* order all selected droids with two a locations and a stat */
void orderSelectedStatsTwoLoc(UDWORD player, DROID_ORDER order,
        BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2, BOOL add)
{
	DROID		*psCurr;

//turn off the build queue availability until desired release date!
#ifdef DISABLE_BUILD_QUEUE
    add = false;
#endif

	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->selected)
		{
            if (add)
            {
    			orderDroidStatsTwoLocAdd(psCurr, order, psStats, x1,y1, x2,y2);
            }
            else
            {
                orderDroidStatsTwoLoc(psCurr, order, psStats, x1,y1, x2,y2);
            }
		}
	}
}


// See if the player has access to a transporter in this map.
//
DROID *FindATransporter(void)
{
	DROID *psDroid;

	for(psDroid = apsDroidLists[selectedPlayer]; (psDroid != NULL); psDroid = psDroid->psNext) {
		if( psDroid->droidType == DROID_TRANSPORTER ) {
			return psDroid;
		}
	}

	return NULL;
}


// See if the player has access to a factory in this map.
//
static STRUCTURE *FindAFactory(UDWORD player, UDWORD factoryType)
{
	STRUCTURE *psStruct;

	ASSERT( player < MAX_PLAYERS,
		"FindAFactory: invalid player number" );

	for(psStruct = apsStructLists[player]; psStruct != NULL; psStruct = psStruct->psNext)
	{
		if(psStruct->pStructureType->type == factoryType)
		{
			return psStruct;
		}
	}

	return NULL;
}


// See if the player has access to a repair facility in this map.
//
static STRUCTURE *FindARepairFacility(void)
{
	STRUCTURE *psStruct;

	for(psStruct = apsStructLists[selectedPlayer]; (psStruct != NULL); psStruct = psStruct->psNext) {
		if(psStruct->pStructureType->type == REF_REPAIR_FACILITY) {
			return psStruct;
		}
	}

	return NULL;
}


// see if a droid supports a secondary order
BOOL secondarySupported(DROID *psDroid, SECONDARY_ORDER sec)
{
	BOOL supported;

	supported = true;	// Default to supported.

	switch (sec)
	{
	case DSO_ASSIGN_PRODUCTION:
	case DSO_ASSIGN_CYBORG_PRODUCTION:
	case DSO_ASSIGN_VTOL_PRODUCTION:
	case DSO_CLEAR_PRODUCTION:		// remove production from a command droid
	case DSO_FIRE_DESIGNATOR:
		if (psDroid->droidType != DROID_COMMAND)
		{
			supported = false;
		}
		if ((sec == DSO_ASSIGN_PRODUCTION && FindAFactory(psDroid->player, REF_FACTORY) == NULL) ||
			(sec == DSO_ASSIGN_CYBORG_PRODUCTION && FindAFactory(psDroid->player, REF_CYBORG_FACTORY) == NULL) ||
			(sec == DSO_ASSIGN_VTOL_PRODUCTION && FindAFactory(psDroid->player, REF_VTOL_FACTORY) == NULL))
		{
			supported = false;
		}
        //don't allow factories to be assigned to commanders during a Limbo Expand mission
        if ((sec == DSO_ASSIGN_PRODUCTION || sec == DSO_ASSIGN_CYBORG_PRODUCTION ||
            sec == DSO_ASSIGN_VTOL_PRODUCTION) && missionLimboExpand())
        {
            supported = false;
        }
		break;

	case DSO_ATTACK_RANGE:
	case DSO_ATTACK_LEVEL:
		if (psDroid->droidType == DROID_REPAIR ||
            psDroid->droidType == DROID_CYBORG_REPAIR)
		{
			supported = false;
		}
		if (psDroid->droidType == DROID_CONSTRUCT ||
            psDroid->droidType == DROID_CYBORG_CONSTRUCT)
		{
			supported = false;
		}
		break;

	case DSO_CIRCLE:
		if (!isVtolDroid(psDroid))
		{
			supported = false;
		}
		break;

	case DSO_REPAIR_LEVEL:
	case DSO_PATROL:
	case DSO_HALTTYPE:
	case DSO_RETURN_TO_LOC:
		break;

/*	case DSO_RETURN_TO_REPAIR:	// Only if player has got a repair facility.
		if(FindARepairFacility() == NULL) {
			supported = false;
		}
		break;*/

	case DSO_RECYCLE:			// Only if player has got a factory.
		if ((FindAFactory(psDroid->player, REF_FACTORY) == NULL) &&
			(FindAFactory(psDroid->player, REF_CYBORG_FACTORY) == NULL) &&
			(FindAFactory(psDroid->player, REF_VTOL_FACTORY) == NULL) &&
			(FindARepairFacility() == NULL))
		{
			supported = false;
		}
		break;

/*	case DSO_EMBARK:			// Only if player has got a transporter.
		if(FindATransporter() == NULL) {
			supported = false;
		}
		break;*/

	default:
		supported = false;
		break;
	}

	return supported;
}


// get the state of a secondary order, return false if unsupported
BOOL secondaryGetState(DROID *psDroid, SECONDARY_ORDER sec, SECONDARY_STATE *pState)
{
	SECONDARY_STATE	state;

	state = psDroid->secondaryOrder;

	switch (sec)
	{
	case DSO_ATTACK_RANGE:
		*pState = (SECONDARY_STATE)(state & DSS_ARANGE_MASK);
		break;
	case DSO_REPAIR_LEVEL:
		*pState = (SECONDARY_STATE)(state & DSS_REPLEV_MASK);
		break;
	case DSO_ATTACK_LEVEL:
		*pState = (SECONDARY_STATE)(state & DSS_ALEV_MASK);
		break;
	case DSO_ASSIGN_PRODUCTION:
	case DSO_ASSIGN_CYBORG_PRODUCTION:
	case DSO_ASSIGN_VTOL_PRODUCTION:
		*pState = (SECONDARY_STATE)(state & DSS_ASSPROD_MASK);
		break;
	case DSO_RECYCLE:
		*pState = (SECONDARY_STATE)(state & DSS_RECYCLE_MASK);
		break;
	case DSO_PATROL:
		*pState = (SECONDARY_STATE)(state & DSS_PATROL_MASK);
		break;
	case DSO_CIRCLE:
		*pState = (SECONDARY_STATE)(state & DSS_CIRCLE_MASK);
		break;
	case DSO_HALTTYPE:
		*pState = (SECONDARY_STATE)(state & DSS_HALT_MASK);
		break;
	case DSO_RETURN_TO_LOC:
		*pState = (SECONDARY_STATE)(state & DSS_RTL_MASK);
		break;
	case DSO_FIRE_DESIGNATOR:
//		*pState = state & DSS_FIREDES_MASK;
		if (cmdDroidGetDesignator(psDroid->player) == psDroid)
		{
			*pState = DSS_FIREDES_SET;
		}
		else
		{
			*pState = DSS_NONE;
		}
		break;
	default:
		*pState = DSS_NONE;
		break;
	}

	return true;
}


#ifdef DEBUG
static char *secondaryPrintFactories(UDWORD state)
{
	SDWORD		i;
	static		char aBuff[255];

	memset(aBuff, 0, sizeof(aBuff));
	for(i=0; i<5; i++)
	{
		if (state & (1 << (i + DSS_ASSPROD_SHIFT)))
		{
			aBuff[i] = (char)('0' + i);
		}
		else
		{
			aBuff[i] = ' ';
		}
		if (state & (1 << (i + DSS_ASSPROD_CYBORG_SHIFT)))
		{
			aBuff[i*2 + 5] = 'c';
			aBuff[i*2 + 6] = (char)('0' + i);
		}
		else
		{
			aBuff[i*2 + 5] = ' ';
			aBuff[i*2 + 6] = ' ';
		}
	}

	return aBuff;
}
#else
#define secondaryPrintFactories(x)
#endif


// check the damage level of a droid against it's secondary state
void secondaryCheckDamageLevel(DROID *psDroid)
{
	SECONDARY_STATE	State;
    unsigned int repairLevel;

	if( secondaryGetState(psDroid, DSO_REPAIR_LEVEL, &State) )
	{
		if (State == DSS_REPLEV_LOW)
		{
			repairLevel = REPAIRLEV_HIGH;			//repair often
		}
		else if(State == DSS_REPLEV_HIGH)
		{
			repairLevel = REPAIRLEV_LOW;	 		// don't repair often.
		}
		else
		{
			repairLevel = 0;						//never repair
		}

        //don't bother checking if 'do or die'
		if( repairLevel && PERCENT(psDroid->body,psDroid->originalBody) <= repairLevel)
		{
			if (psDroid->selected)
			{
				DeSelectDroid(psDroid);
			}
			if (!isVtolDroid(psDroid))
			{
				psDroid->group = UBYTE_MAX;
			}

			/* set return to repair if not on hold */
			if ( psDroid->order != DORDER_RTR &&
				 psDroid->order != DORDER_RTB &&
				 !vtolRearming(psDroid))
			{
				if (isVtolDroid(psDroid))
				{
					moveToRearm(psDroid);
				}
				else
				{
					orderDroid(psDroid, DORDER_RTR);
				}
			}
		}
	}
}


// set the state of a secondary order, return false if failed.
BOOL secondarySetState(DROID *psDroid, SECONDARY_ORDER sec, SECONDARY_STATE State)
{
	UDWORD		CurrState, factType, prodType;
	STRUCTURE	*psStruct;
	SDWORD		factoryInc, order;
	BOOL		retVal, bMultiPlayGame = false;
	DROID		*psTransport, *psCurr, *psNext;



	if(bMultiPlayer)
	{
        //store the value before overwriting
        bMultiPlayGame = bMultiPlayer;
		sendDroidSecondary(psDroid,sec,State);
		turnOffMultiMsg(true);		// msgs off.
	}


	// set the state for any droids in the command group
	if ((sec != DSO_RECYCLE) &&
		psDroid->droidType == DROID_COMMAND &&
		psDroid->psGroup != NULL &&
		psDroid->psGroup->type == GT_COMMAND)
	{
		grpSetSecondary(psDroid->psGroup, sec, State);
	}

	CurrState = psDroid->secondaryOrder;

	retVal = true;
	switch (sec) {
		case DSO_ATTACK_RANGE:
			CurrState = (CurrState & ~DSS_ARANGE_MASK) | State;
			break;

		case DSO_REPAIR_LEVEL:
			CurrState = (CurrState & ~DSS_REPLEV_MASK) | State;
			psDroid->secondaryOrder = CurrState;
			secondaryCheckDamageLevel(psDroid);
			break;

		case DSO_ATTACK_LEVEL:
			CurrState = (CurrState & ~DSS_ALEV_MASK) | State;
			if (State == DSS_ALEV_NEVER)
			{
				if ( orderState(psDroid, DORDER_ATTACK) )// ||
//					 orderState(psDroid, DORDER_FIRESUPPORT) )
				{
					// just kill these orders
					orderDroid(psDroid, DORDER_STOP);
					if (isVtolDroid(psDroid))
					{
						moveToRearm(psDroid);
					}
				}
				else if ( orderState(psDroid, DORDER_GUARD) &&
						  droidAttacking(psDroid))
				{
					// send the unit back to the guard position
					actionDroid(psDroid, DACTION_NONE);
				}
				else if ( orderState(psDroid, DORDER_PATROL) )
				{
					// send the unit back to the patrol
					actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->actionX, psDroid->actionY);
				}
			}
			break;


		case DSO_ASSIGN_PRODUCTION:
		case DSO_ASSIGN_CYBORG_PRODUCTION:
		case DSO_ASSIGN_VTOL_PRODUCTION:
#ifdef DEBUG
			debug( LOG_NEVER, "order factories %s\n", secondaryPrintFactories(State));
#endif
			if ( sec == DSO_ASSIGN_PRODUCTION)
			{
				prodType = REF_FACTORY;
			}
			else if ( sec == DSO_ASSIGN_CYBORG_PRODUCTION)
			{
				prodType = REF_CYBORG_FACTORY;
			}
			else
			{
				prodType = REF_VTOL_FACTORY;
			}

			if (psDroid->droidType == DROID_COMMAND)
			{
				// look for the factories
				for (psStruct = apsStructLists[psDroid->player]; psStruct;
					 psStruct=psStruct->psNext)
				{
					factType = psStruct->pStructureType->type;
					if ( factType == REF_FACTORY ||
						 factType == REF_VTOL_FACTORY ||
						 factType == REF_CYBORG_FACTORY )
					{
						factoryInc = ((FACTORY*)psStruct->pFunctionality)->psAssemblyPoint->factoryInc;
						if (factType == REF_FACTORY)
						{
							factoryInc += DSS_ASSPROD_SHIFT;
						}
						else if ( factType == REF_CYBORG_FACTORY )
						{
							factoryInc += DSS_ASSPROD_CYBORG_SHIFT;
						}
						else
						{
							factoryInc += DSS_ASSPROD_VTOL_SHIFT;
						}
						if ( !( CurrState & ( 1 << factoryInc) ) &&
							  ( State & ( 1 << factoryInc) ) )
						{
							assignFactoryCommandDroid(psStruct, psDroid);// assign this factory to the command droid
						}
						else if ( ( prodType == factType ) &&
								  ( CurrState & ( 1 << factoryInc) ) &&
								  !( State & ( 1 << factoryInc) ) )
						{
							// remove this factory from the command droid
							assignFactoryCommandDroid(psStruct, NULL);
						}
					}
				}
				if (prodType == REF_FACTORY)
				{
					CurrState &= ~DSS_ASSPROD_FACT_MASK;
				}
				else if (prodType == REF_CYBORG_FACTORY)
				{
					CurrState &= ~DSS_ASSPROD_CYB_MASK;
				}
				else
				{
					CurrState &= ~DSS_ASSPROD_VTOL_MASK;
				}
				CurrState |= (State & DSS_ASSPROD_MASK);
#ifdef DEBUG
				debug( LOG_NEVER, "final factories %s\n", secondaryPrintFactories(CurrState));
#endif
			}
			break;

		case DSO_CLEAR_PRODUCTION:
			if (psDroid->droidType == DROID_COMMAND)
			{
				// simply clear the flag - all the factory stuff is done in assignFactoryCommandDroid
				CurrState &= ~ (State & DSS_ASSPROD_MASK);
			}
			break;


		case DSO_RECYCLE:
			if(State & DSS_RECYCLE_MASK)
			{
				if (!orderState(psDroid, DORDER_RECYCLE))
				{
					orderDroid(psDroid, DORDER_RECYCLE);
				}
//				CurrState &= ~(DSS_HOLD_SET|DSS_RTB_SET|DSS_RTR_SET|DSS_RECYCLE_SET);
				CurrState &= ~(DSS_RTL_MASK|DSS_RECYCLE_MASK|DSS_HALT_MASK);
				CurrState |= DSS_RECYCLE_SET|DSS_HALT_GUARD;
				psDroid->group = UBYTE_MAX;
				if (psDroid->psGroup != NULL)
				{
					if (psDroid->droidType == DROID_COMMAND)
					{
						// remove all the units from the commanders group
						for (psCurr = psDroid->psGroup->psList; psCurr; psCurr=psNext)
						{
							psNext = psCurr->psGrpNext;
							grpLeave(psCurr->psGroup, psCurr);
							orderDroid(psCurr, DORDER_STOP);
						}
					}
					else if (psDroid->psGroup->type == GT_COMMAND)
					{
						grpLeave(psDroid->psGroup, psDroid);
					}
				}
			}
			else
			{
				if (orderState(psDroid, DORDER_RECYCLE))
				{
					orderDroid(psDroid, DORDER_STOP);
				}
				CurrState &= ~DSS_RECYCLE_MASK;
			}
			break;
		case DSO_CIRCLE:
			if (State & DSS_CIRCLE_SET)
			{
				CurrState |= DSS_CIRCLE_SET;
			}
			else
			{
				CurrState &= ~DSS_CIRCLE_MASK;
			}
			break;
		case DSO_PATROL:
			if (State & DSS_PATROL_SET)
			{
				CurrState |= DSS_PATROL_SET;
			}
			else
			{
				CurrState &= ~DSS_PATROL_MASK;
			}
			break;
		case DSO_HALTTYPE:
			switch (State & DSS_HALT_MASK)
			{
			case DSS_HALT_PERSUE:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_PERSUE;
				if (orderState(psDroid, DORDER_GUARD))
				{
					orderDroid(psDroid, DORDER_STOP);
				}
				break;
			case DSS_HALT_GUARD:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_GUARD;
				orderDroidLoc(psDroid, DORDER_GUARD, psDroid->pos.x,psDroid->pos.y);
				break;
			case DSS_HALT_HOLD:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_HOLD;
				if (!orderState(psDroid, DORDER_FIRESUPPORT))
				{
					orderDroid(psDroid, DORDER_STOP);
				}
				break;
			}
			break;
		case DSO_RETURN_TO_LOC:
			if ((State & DSS_RTL_MASK) == 0)
			{
				if (orderState(psDroid, DORDER_RTR) ||
					orderState(psDroid, DORDER_RTB) ||
					orderState(psDroid, DORDER_EMBARK))
				{
					orderDroid(psDroid, DORDER_STOP);
				}
				CurrState &= ~DSS_RTL_MASK;
			}
			else
			{
				order = DORDER_NONE;
				CurrState &= ~DSS_RTL_MASK;
				if ((CurrState & DSS_HALT_MASK) == DSS_HALT_HOLD)
				{
					CurrState &= ~DSS_HALT_MASK;
					CurrState |= DSS_HALT_GUARD;
				}
				switch (State & DSS_RTL_MASK)
				{
				case DSS_RTL_REPAIR:
//					if (FindARepairFacility() != NULL)
					{
						order = DORDER_RTR;
						CurrState |= DSS_RTL_REPAIR;
						// can't clear the selection here cos it breaks
						// the secondary order screen
//						psDroid->selected = false;
//						psDroid->group = UBYTE_MAX;
					}
//					else
//					{
//						retVal = false;
//					}
					break;
				case DSS_RTL_BASE:
					order = DORDER_RTB;
					CurrState |= DSS_RTL_BASE;
					break;
				case DSS_RTL_TRANSPORT:
					psTransport = FindATransporter();
					if (psTransport != NULL)
					{
                        //in multiPlayer can only put cyborgs onto a Transporter
                        if (bMultiPlayGame && !cyborgDroid(psDroid))
                        {
                            retVal = false;
                        }
                        else
                        {
						    order = DORDER_EMBARK;
						    CurrState |= DSS_RTL_TRANSPORT;
						    if (!orderState(psDroid, DORDER_EMBARK))
						    {
							    orderDroidObj(psDroid, DORDER_EMBARK, (BASE_OBJECT *)psTransport);
						    }
                        }
					}
					else
					{
						retVal = false;
					}
					break;
				default:
					order = DORDER_NONE;
					break;
				}
				if (!orderState(psDroid, order))
				{
					orderDroid(psDroid, order);
				}
			}
			break;

		case DSO_FIRE_DESIGNATOR:
			// don't actually set any secondary flags - the cmdDroid array is
			// always used to determine which commander is the designator
			if (State & DSS_FIREDES_SET)
			{
				cmdDroidSetDesignator(psDroid);
			}
			else if (cmdDroidGetDesignator(psDroid->player) == psDroid)
			{
				cmdDroidClearDesignator(psDroid->player);
			}
			break;

		default:
			break;
	}

	psDroid->secondaryOrder = CurrState;


	turnOffMultiMsg(false);


	return retVal;
}


// deal with a droid receiving a primary order
BOOL secondaryGotPrimaryOrder(DROID *psDroid, DROID_ORDER order)
{
	UDWORD	oldState;

	if (psDroid->droidType == DROID_TRANSPORTER)
	{
		return false;
	}

	if (order != DORDER_NONE &&
		order != DORDER_STOP &&
		order != DORDER_DESTRUCT &&
		order != DORDER_GUARD)
	{
		//reset 2ndary order
		oldState = psDroid->secondaryOrder;
		psDroid->secondaryOrder &= ~ (DSS_RTL_MASK|DSS_RECYCLE_MASK|DSS_PATROL_MASK);


		if((oldState != psDroid->secondaryOrder) &&
		   (psDroid->player == selectedPlayer))
		{
			intRefreshScreen();
		}
	}

	return false;
}


// set the state of a numeric group
static void secondarySetGroupState(UDWORD player, UDWORD group, SECONDARY_ORDER sec, SECONDARY_STATE state)
{
	DROID	*psCurr;
	SECONDARY_STATE	currState;

	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->group == group &&
			secondaryGetState(psCurr, sec, &currState) && (currState != state))
		{
			secondarySetState(psCurr, sec, state);
		}
	}
}

// get the average secondary state of a numeric group
static SECONDARY_STATE secondaryGetAverageGroupState(UDWORD player, UDWORD group, UDWORD mask)
{
#define MAX_STATES		5
	struct { UDWORD state, num; } aStateCount[MAX_STATES];
	SDWORD	i, numStates, max;
	DROID	*psCurr;

	// count the number of units for each state
	numStates = 0;
	memset(aStateCount, 0, sizeof(aStateCount));
	for(psCurr=apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (psCurr->group == group)
		{
			for (i=0; i<numStates; i++)
			{
				if (aStateCount[i].state == (psCurr->secondaryOrder & mask))
				{
					aStateCount[i].num += 1;
					break;
				}
			}

			if (i == numStates)
			{
				aStateCount[numStates].state = psCurr->secondaryOrder & mask;
				aStateCount[numStates].num = 1;
				numStates += 1;
			}
		}
	}

	max = 0;
	for (i=0; i<numStates; i++)
	{
		if (aStateCount[i].num > aStateCount[max].num)
		{
			max = i;
		}
	}

	return aStateCount[max].state;
}


// make all the members of a numeric group have the same secondary states
void secondarySetAverageGroupState(UDWORD player, UDWORD group)
{
	// lookup table for orders and masks
	#define MAX_ORDERS	4
	struct { UDWORD order, mask; } aOrders[MAX_ORDERS] =
	{
		{ DSO_ATTACK_RANGE, DSS_ARANGE_MASK },
		{ DSO_REPAIR_LEVEL, DSS_REPLEV_MASK },
		{ DSO_ATTACK_LEVEL, DSS_ALEV_MASK },
		{ DSO_HALTTYPE, DSS_HALT_MASK }
	};
	SDWORD	i, state;

	for(i=0; i<MAX_ORDERS; i++)
	{
		state = secondaryGetAverageGroupState(player, group, aOrders[i].mask);
		secondarySetGroupState(player, group, aOrders[i].order, state);
	}
}


// do a moral check for a player
void orderMoralCheck(UDWORD player)
{
	DROID	*psCurr;
	SDWORD	units, numVehicles, leadership, personLShip, check;

	// count the number of vehicles and units on the side
	units=0;
	numVehicles = 0;
	for(psCurr=apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		units += 1;
		if (psCurr->droidType != DROID_PERSON)
		{
			numVehicles += 1;
		}
	}

	if (units > asRunData[player].forceLevel)
	{
		// too many units, don't run
		return;
	}
// 	debug( LOG_NEVER, "moral check for player %d\n", player );

	// calculate the overall leadership
	leadership = asRunData[player].leadership + 10;
	personLShip = asRunData[player].leadership + numVehicles * 3;

	// do the moral check for each droid
	for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
	{
		if (orderState(psCurr, DORDER_RUN) ||
			orderState(psCurr, DORDER_RUNBURN) ||
			orderState(psCurr, DORDER_RETREAT) ||
			orderState(psCurr, DORDER_RTB) ||
			orderState(psCurr, DORDER_RTR) ||
			orderState(psCurr, DORDER_DESTRUCT))
		{
			// already running - ignore
			continue;
		}

		check = rand() % 100;
		if (psCurr->droidType == DROID_PERSON)
		{
			if (check > personLShip)
			{
// 				debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psCurr->id );
				orderDroid(psCurr, DORDER_RUN);
			}
		}
		else
		{
			if (check > leadership)
			{
// 				debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psCurr->id );
				orderDroid(psCurr, DORDER_RUN);
			}
		}
	}
}

// do a moral check for a group
void orderGroupMoralCheck(DROID_GROUP *psGroup)
{
	DROID		*psCurr;
	SDWORD		units, numVehicles, leadership, personLShip, check;
	RUN_DATA	*psRunData;

	// count the number of vehicles and units on the side
	units=0;
	numVehicles = 0;
	for(psCurr=psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
	{
		units += 1;
		if (psCurr->droidType != DROID_PERSON)
		{
			numVehicles += 1;
		}
	}

	psRunData = &psGroup->sRunData;
	if (units > psRunData->forceLevel)
	{
		// too many units, don't run
		return;
	}

	// calculate the overall leadership
	leadership = psRunData->leadership + 10;
	personLShip = psRunData->leadership + numVehicles * 3;

	// do the moral check for each droid
	for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
	{
		if (orderState(psCurr, DORDER_RUN) ||
			orderState(psCurr, DORDER_RUNBURN) ||
			orderState(psCurr, DORDER_RETREAT) ||
			orderState(psCurr, DORDER_RTB) ||
			orderState(psCurr, DORDER_RTR) ||
			orderState(psCurr, DORDER_DESTRUCT))
		{
			// already running - ignore
			continue;
		}

		check = rand() % 100;
		if (psCurr->droidType == DROID_PERSON)
		{
			if (check > personLShip)
			{
// 				debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psCurr->id );
				orderDroidLoc(psCurr, DORDER_RUN, psRunData->sPos.x, psRunData->sPos.y);
			}
		}
		else
		{
			if (check > leadership)
			{
// 				debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psCurr->id );
				orderDroidLoc(psCurr, DORDER_RUN, psRunData->sPos.x, psRunData->sPos.y);
			}
		}
	}
}

// do a health check for a droid
void orderHealthCheck(DROID *psDroid)
{
	DROID		*psCurr;
    SBYTE       healthLevel = 0;
    UDWORD      retreatX = 0, retreatY = 0;

	if (psDroid->droidType == DROID_TRANSPORTER)
	{
		return;
	}

    //get the health value to compare with
    if (psDroid->psGroup)
    {
        healthLevel = psDroid->psGroup->sRunData.healthLevel;
        retreatX = psDroid->psGroup->sRunData.sPos.x;
        retreatY = psDroid->psGroup->sRunData.sPos.y;
    }

    //if health has not been set for the group - use players'
    if (!healthLevel)
    {
        healthLevel = asRunData[psDroid->player].healthLevel;
    }

    //if not got a health level set then ignore
    if (!healthLevel)
    {
        return;
    }

    //if pos has not been set for the group - use players'
    if (retreatX == 0 && retreatY == 0)
    {
        retreatX = asRunData[psDroid->player].sPos.x;
        retreatY = asRunData[psDroid->player].sPos.y;
    }

    if (PERCENT(psDroid->body, psDroid->originalBody) < healthLevel)
    {
        //order this droid to turn and run - // if already running - ignore
		if (!(orderState(psDroid, DORDER_RUN) ||
			orderState(psDroid, DORDER_RUNBURN) ||
			orderState(psDroid, DORDER_RETREAT) ||
			orderState(psDroid, DORDER_RTB) ||
			orderState(psDroid, DORDER_RTR) ||
			orderState(psDroid, DORDER_DESTRUCT)))
		{
//         	debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psDroid->id );
		    orderDroidLoc(psDroid, DORDER_RUN, retreatX, retreatY);
        }

	    // order each unit in the same group to run
        if (psDroid->psGroup)
        {
	        for(psCurr = psDroid->psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
	        {
		        if (orderState(psCurr, DORDER_RUN) ||
			        orderState(psCurr, DORDER_RUNBURN) ||
			        orderState(psCurr, DORDER_RETREAT) ||
			        orderState(psCurr, DORDER_RTB) ||
			        orderState(psCurr, DORDER_RTR) ||
			        orderState(psCurr, DORDER_DESTRUCT))
		        {
			        // already running - ignore
			        continue;
		        }

// 		        debug( LOG_NEVER, "   DORDER_RUN: droid %d\n", psCurr->id );
		        orderDroidLoc(psCurr, DORDER_RUN, retreatX, retreatY);
	        }
        }
    }
}

// set the state of a secondary order for a Factory, return false if failed.
BOOL setFactoryState(STRUCTURE *psStruct, SECONDARY_ORDER sec, SECONDARY_STATE State)
{
	UDWORD		CurrState;
	BOOL		retVal;
    FACTORY     *psFactory;


    if (!StructIsFactory(psStruct))
    {
        ASSERT( false, "setFactoryState: structure is not a factory" );
        return false;
    }

    psFactory = (FACTORY *)psStruct->pFunctionality;

	CurrState = psFactory->secondaryOrder;

	retVal = true;
	switch (sec) {
		case DSO_ATTACK_RANGE:
			CurrState = (CurrState & ~DSS_ARANGE_MASK) | State;
			break;

		case DSO_REPAIR_LEVEL:
			CurrState = (CurrState & ~DSS_REPLEV_MASK) | State;
			break;

		case DSO_ATTACK_LEVEL:
			CurrState = (CurrState & ~DSS_ALEV_MASK) | State;
			break;

		case DSO_PATROL:
			if (State & DSS_PATROL_SET)
			{
				CurrState |= DSS_PATROL_SET;
			}
			else
			{
				CurrState &= ~DSS_PATROL_MASK;
			}
			break;
		case DSO_HALTTYPE:
			switch (State & DSS_HALT_MASK)
			{
			case DSS_HALT_PERSUE:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_PERSUE;
				break;
			case DSS_HALT_GUARD:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_GUARD;
				break;
			case DSS_HALT_HOLD:
				CurrState &= ~ DSS_HALT_MASK;
				CurrState |= DSS_HALT_HOLD;
				break;
			}
			break;
		default:
			break;
	}

	psFactory->secondaryOrder = CurrState;

	return retVal;
}

// get the state of a secondary order for a Factory, return false if unsupported
BOOL getFactoryState(STRUCTURE *psStruct, SECONDARY_ORDER sec, SECONDARY_STATE *pState)
{
	UDWORD	state;

    if (!StructIsFactory(psStruct))
    {
        ASSERT( false, "getFactoryState: structure is not a factory" );
        return false;
    }

    state = ((FACTORY *)psStruct->pFunctionality)->secondaryOrder;

	switch (sec)
	{
	case DSO_ATTACK_RANGE:
		*pState = (SECONDARY_STATE)(state & DSS_ARANGE_MASK);
		break;
	case DSO_REPAIR_LEVEL:
		*pState = (SECONDARY_STATE)(state & DSS_REPLEV_MASK);
		break;
	case DSO_ATTACK_LEVEL:
		*pState = (SECONDARY_STATE)(state & DSS_ALEV_MASK);
		break;
	case DSO_PATROL:
		*pState = (SECONDARY_STATE)(state & DSS_PATROL_MASK);
		break;
	case DSO_HALTTYPE:
		*pState = (SECONDARY_STATE)(state & DSS_HALT_MASK);
		break;
	default:
		*pState = 0;
		break;
	}

	return true;
}

//lasSat structure can select a target
void orderStructureObj(UDWORD player, BASE_OBJECT *psObj)
{
    STRUCTURE   *psStruct;
	UDWORD		firePause, damLevel;

    for (psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
    {
        if (lasSatStructSelected(psStruct))
        {
            //there will only be the one!
            firePause = weaponFirePause(&asWeaponStats[psStruct->asWeaps[0].
                nStat], (UBYTE)player);
            damLevel = PERCENT(psStruct->body, structureBody(psStruct));
	        if (damLevel < HEAVY_DAMAGE_LEVEL)
	        {
		        firePause += firePause;
	        }
	        if (isHumanPlayer(player)
				&& (gameTime - psStruct->asWeaps[0].lastFired <= firePause) )
			{
		       /* Too soon to fire again */
		        break;
			}
            //ok to fire - so fire away
            proj_SendProjectile(&psStruct->asWeaps[0], NULL,
                player, psObj->pos.x, psObj->pos.y, psObj->pos.z, psObj, true, false, 0);
            //set up last fires time
            psStruct->asWeaps[0].lastFired =  gameTime;

            //play 5 second countdown message
			audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN,
				psObj->pos.x, psObj->pos.y, psObj->pos.z );


			// send the weapon fire
			if(bMultiPlayer)
			{
				sendLasSat(player,psStruct,psObj);
			}

			break;
        }
    }
}

const char* getDroidOrderName(DROID_ORDER order)
{
	static const char* name[] =
	{
		"DORDER_NONE",				// no order set
		"DORDER_STOP",				// stop the current order
		"DORDER_MOVE",				// 2 - move to a location
		"DORDER_ATTACK",				// attack an enemy
		"DORDER_BUILD",				// 4 - build a structure
		"DORDER_HELPBUILD",			// help to build a structure
		"DORDER_LINEBUILD",			// 6 - build a number of structures in a row (walls + bridges)
		"DORDER_DEMOLISH",			// demolish a structure
		"DORDER_REPAIR",				// 8 - repair a structure
		"DORDER_OBSERVE",				// keep a target in sensor view
		"DORDER_FIRESUPPORT",			// 10 - attack whatever the linked sensor droid attacks
		"DORDER_RETREAT",				// return to the players retreat position
		"DORDER_DESTRUCT",			// 12 - self destruct
		"DORDER_RTB",					// return to base
		"DORDER_RTR",					// 14 - return to repair at any repair facility
		"DORDER_RUN",					// run away after moral failure
		"DORDER_EMBARK",				// 16 - board a transporter
		"DORDER_DISEMBARK",			// get off a transporter
		"DORDER_ATTACKTARGET",		// 18 - a suggestion to attack something
									// i.e. the target was chosen because the droid could see it
		"DORDER_COMMAND",				// a command droid issuing orders to it's group
		"DORDER_BUILDMODULE",			// 20 - build a module (power, research or factory)
		"DORDER_RECYCLE",				// return to factory to be recycled
		"DORDER_TRANSPORTOUT",		// 22 - offworld transporter order
		"DORDER_TRANSPORTIN",			// onworld transporter order
		"DORDER_TRANSPORTRETURN",		// 24 - transporter return after unloading
		"DORDER_GUARD",				// guard a structure
		"DORDER_DROIDREPAIR",			// 26 - repair a droid
		"DORDER_RESTORE",				// restore resistance points for a structure
		"DORDER_SCOUT",				// 28 - same as move, but stop if an enemy is seen
		"DORDER_RUNBURN",				// run away on fire
		"DORDER_CLEARWRECK",			// 30 - constructor droid to clear up building wreckage
		"DORDER_PATROL",				// move between two way points
		"DORDER_REARM",				// 32 - order a vtol to rearming pad
		"DORDER_MOVE_ATTACKWALL",		// move to a location taking out a blocking wall on the way
		"DORDER_SCOUT_ATTACKWALL",	// 34 - scout to a location taking out a blocking wall on the way
		"DORDER_RECOVER",				// pick up an artifact
		"DORDER_LEAVEMAP",			// 36 - vtol flying off the map
		"DORDER_RTR_SPECIFIED",		// return to repair at a specified repair center
		"DORDER_UNDEFINED",
		"DORDER_UNDEFINED2",
		"DORDER_CIRCLE"				// circles target location and engage
	};

	ASSERT(order < sizeof(name) / sizeof(name[0]), "DROID_ORDER out of range: %u", order);

	return name[order];
}
